Snippets

Sergio de EOM CakePHP 3.1 Login for custom (Model + Form + Bootstrap + Bootstrap for Spanish in Inflector)

You are viewing an old version of this snippet. View the current version.
Revised by Sergio N. 73f520a
<?php
/**
 * CakePHP(tm) Path File: \App\Controller\AppController.php
 */
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\Event;

/**
 * Application Controller
 * Add your application-wide methods in the class below, your controllers
 * will inherit them.
 * @link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller
 */
class AppController extends Controller
{

    /**
     * Initialization hook method.
     * Use this method to add common initialization code like loading components.
     * e.g. `$this->loadComponent('Security');`
     * @return void
     */
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');
        // Set Full Auth via Form
        $this->loadComponent('Auth', [
            'authorize' => ['Controller'],
            'loginRedirect' => [
                'controller' => 'Pages', // @todo Mi Controller segun PROYECTO a modo de demo
                'action' => 'display',
                'home'
            ],
            'logoutRedirect' => [
                'controller' => 'Usuarios', // @todo Mi Controller segun PROYECTO
                'action' => 'login'
            ],
            'loginAction' => [
                'controller' => 'Usuarios', // @todo Mi Controller segun PROYECTO
                'action' => 'login'
            ],
            'authenticate' => [
                'Form' => [
                    //'passwordHasher' => 'Blowfish',
                    'userModel' => 'Usuarios',                                         // @todo Mi TABLA segun DB
                    'fields' => ['username' => 'email', 'password' => 'password'],     // @todo mis campos personalizados segun DB
                    'scope' => ['Usuarios.habilitado' => 1]                            // @todo Filtro para bloquiar ingresos de usuarios activos segun DB
                ]
            ],
            'authError' => '¿De verdad crees que se le permita ver eso?',
            'storage' => 'Session'
        ]);
        
    }

    /**
     * @param $usuario
     *
     * @return bool
     */
    public function isAuthorized($usuario = array())
    {
        // Tipo de permiso es Admin..?
        if (true === isset($usuario['perfiles_id']) && $usuario['perfiles_id'] === '1') { // @todo Codigo a cambiar segun DB y PROYECTO
            // Permitir
            return true;
        } else {
            // Denegar
            return false;
        }
    }

    /**
     * @param Event $event An Event instance
     *
     * @return void
     */
    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);

        // Bloquiar todo
        $this->Auth->deny();
        // Es Admin..?
        if ($this->Auth->user('perfiles_id') === 1) { // @todo Codigo a cambiar segun DB y PROYECTO
            // Permitir todo al Admin
            $this->Auth->allow();
        } else {
            // Es anonimo..?
            $this->Auth->allow(['index', 'view', 'display', 'contactarnos', 'registrarce', 'logout']); // @todo Codigo a cambiar segun DB y PROYECTO
        }
    }


    /**
     * Before render callback.
     *
     * @param \Cake\Event\Event $event The beforeRender event.
     *
     * @return void
     */
    public function beforeRender(Event $event)
    {
        if (!array_key_exists('_serialize', $this->viewVars) &&
            in_array($this->response->type(), ['application/json', 'application/xml'])
        ) {
            $this->set('_serialize', true);
        }
    }
}
<?php
// Path File: \App\src\Model\Entity\Usuario.php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;

/**
 * Usuario Entity.
 * @property int $id
 * @property string $email
 * @property string $password
 * @property string $nombre
 * @property int $perfiles_id
 * @property \App\Model\Entity\Perfile $perfile
 * @property bool $habilitado
 * @property \Cake\I18n\Time $created
 * @property \Cake\I18n\Time $modified
 */
class Usuario extends Entity
{

    /**
     * Fields that can be mass assigned using newEntity() or patchEntity().
     * Note that when '*' is set to true, this allows all unspecified fields to
     * be mass assigned. For security purposes, it is advised to set '*' to false
     * (or remove it), and explicitly make individual fields accessible as needed.
     * @var array
     */
    protected $_accessible = [
        '*' => true,
        'id' => false,
    ];

    protected function _setPassword($password)
    {
        return (new DefaultPasswordHasher)->hash($password);
    }

}
<?php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

/**
 * Path File: \App\Controller\UsuariosController.php
 * Usuarios Controller
 *
 * @property \App\Model\Table\UsuariosTable $Usuarios
 */
class UsuariosController extends AppController
{
    
    public function logout(){
        return $this->redirect($this->Auth->logout());
    }

    public function login(){
        if ($this->request->is('post')) {
            // Existe el usuario ..?
            $user = $this->Auth->identify();
            if ($user != false) {
                // Set Storage
                $this->Auth->setUser($user);
                if ($this->Auth->authenticationProvider()->needsPasswordRehash()) {
                    $user = $this->Users->get($this->Auth->user('id'));
                    $user->password = $this->request->data('password');
                    // Save Usuario
                    $this->Users->save($user);
                }
                return $this->redirect($this->Auth->redirectUrl());
            }else{
                // Set Flash Auth
                $this->Flash->error( __('Usuario o Clave es incorrecta'), ['key'=>'auth']);
            }
        }
    }

    /**
     * Index method
     *
     * @return void
     */
    public function index()
    {
        $this->paginate = [
            'contain' => ['Perfiles']
        ];
        $this->set('usuarios', $this->paginate($this->Usuarios));
        $this->set('_serialize', ['usuarios']);
    }

    /**
     * View method
     *
     * @param string|null $id Usuario id.
     * @return void
     * @throws \Cake\Network\Exception\NotFoundException When record not found.
     */
    public function view($id = null)
    {
        $usuario = $this->Usuarios->get($id, [
            'contain' => ['Perfiles']
        ]);
        $this->set('usuario', $usuario);
        $this->set('_serialize', ['usuario']);
    }

    /**
     * Add method
     *
     * @return void Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $usuario = $this->Usuarios->newEntity();
        if ($this->request->is('post')) {
            $usuario = $this->Usuarios->patchEntity($usuario, $this->request->data);
            if ($this->Usuarios->save($usuario)) {
                $this->Flash->success(__('The usuario has been saved.'));
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error(__('The usuario could not be saved. Please, try again.'));
            }
        }
        $perfiles = $this->Usuarios->Perfiles->find('list', ['limit' => 200]);
        $this->set(compact('usuario', 'perfiles'));
        $this->set('_serialize', ['usuario']);
    }

    /**
     * Edit method
     *
     * @param string|null $id Usuario id.
     * @return void Redirects on successful edit, renders view otherwise.
     * @throws \Cake\Network\Exception\NotFoundException When record not found.
     */
    public function edit($id = null)
    {
        $usuario = $this->Usuarios->get($id, [
            'contain' => []
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $usuario = $this->Usuarios->patchEntity($usuario, $this->request->data);
            if ($this->Usuarios->save($usuario)) {
                $this->Flash->success(__('The usuario has been saved.'));
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error(__('The usuario could not be saved. Please, try again.'));
            }
        }
        $perfiles = $this->Usuarios->Perfiles->find('list', ['limit' => 200]);
        $this->set(compact('usuario', 'perfiles'));
        $this->set('_serialize', ['usuario']);
    }

    /**
     * Delete method
     *
     * @param string|null $id Usuario id.
     * @return void Redirects to index.
     * @throws \Cake\Network\Exception\NotFoundException When record not found.
     */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $usuario = $this->Usuarios->get($id);
        if ($this->Usuarios->delete($usuario)) {
            $this->Flash->success(__('The usuario has been deleted.'));
        } else {
            $this->Flash->error(__('The usuario could not be deleted. Please, try again.'));
        }
        return $this->redirect(['action' => 'index']);
    }
}
<?php
// Path File: \App\src\Model\Table\UsuariosTable.php
namespace App\Model\Table;

use App\Model\Entity\Usuario;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Auth\DigestAuthenticate;
use Cake\Event\Event;

/**
 * Usuarios Model
 *
 * @property \Cake\ORM\Association\BelongsTo $Perfiles
 */
class UsuariosTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('usuarios');
        $this->displayField('nombre');
        $this->primaryKey('id');

        $this->addBehavior('Timestamp');

        $this->belongsTo('Perfiles', [
            'foreignKey' => 'perfiles_id',
            'joinType' => 'INNER'
        ]);
    }

    public function beforeSave(Event $event)
    {
        $entity = $event->data['entity'];

        // Make a password for digest auth.
        $entity->digest_hash = DigestAuthenticate::password(
            $entity->username,
            $entity->plain_password,
            env('SERVER_NAME')
        );
        return true;
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->add('id', 'valid', ['rule' => 'numeric'])
            ->allowEmpty('id', 'create');

        $validator
            ->add('email', 'valid', ['rule' => 'email'])
            ->requirePresence('email', 'create')
            ->notEmpty('email')
            ->add('email', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']);

        $validator
            ->requirePresence('password', 'create')
            ->notEmpty('password');

        $validator
            ->requirePresence('nombre', 'create')
            ->notEmpty('nombre');

        $validator
            ->add('habilitado', 'valid', ['rule' => 'boolean'])
            ->requirePresence('habilitado', 'create')
            ->notEmpty('habilitado');

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->isUnique(['email']));
        $rules->add($rules->existsIn(['perfiles_id'], 'Perfiles'));
        return $rules;
    }
}
<?php
// Path File: \App\config\bootstrap.php
/**
 * Bootstrap custom for Spanish in Inflector::rules(*******)
 * Create all tables in DB for plural.
 */

/**
 * Configure paths required to find CakePHP + general filepath
 * constants
 */
require __DIR__ . '/paths.php';

// Use composer to load the autoloader.
require ROOT . DS . 'vendor' . DS . 'autoload.php';

/**
 * Bootstrap CakePHP.
 *
 * Does the various bits of setup that CakePHP needs to do.
 * This includes:
 *
 * - Registering the CakePHP autoloader.
 * - Setting the default application paths.
 */
require CORE_PATH . 'config' . DS . 'bootstrap.php';

// You can remove this if you are confident you have intl installed.
if (!extension_loaded('intl')) {
    trigger_error('You must enable the intl extension to use CakePHP.', E_USER_ERROR);
}

use Cake\Cache\Cache;
use Cake\Console\ConsoleErrorHandler;
use Cake\Core\App;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Core\Plugin;
use Cake\Database\Type;
use Cake\Datasource\ConnectionManager;
use Cake\Error\ErrorHandler;
use Cake\Log\Log;
use Cake\Mailer\Email;
use Cake\Network\Request;
use Cake\Routing\DispatcherFactory;
use Cake\Utility\Inflector;
use Cake\Utility\Security;

/**
 * Read configuration file and inject configuration into various
 * CakePHP classes.
 *
 * By default there is only one configuration file. It is often a good
 * idea to create multiple configuration files, and separate the configuration
 * that changes from configuration that does not. This makes deployment simpler.
 */
try {
    Configure::config('default', new PhpConfig());
    Configure::load('app', 'default', false);
} catch (\Exception $e) {
    die($e->getMessage() . "\n");
}

// Load an environment local configuration file.
// You can use a file like app_local.php to provide local overrides to your
// shared configuration.
//Configure::load('app_local', 'default');

// When debug = false the metadata cache should last
// for a very very long time, as we don't want
// to refresh the cache while users are doing requests.
if (!Configure::read('debug')) {
    Configure::write('Cache._cake_model_.duration', '+1 years');
    Configure::write('Cache._cake_core_.duration', '+1 years');
}

/**
 * Set server timezone to UTC. You can change it to another timezone of your
 * choice but using UTC makes time calculations / conversions easier.
 */
date_default_timezone_set('UTC');
//date_default_timezone_set('America/Buenos_Aires');

/**
 * Configure the mbstring extension to use the correct encoding.
 */
mb_internal_encoding(Configure::read('App.encoding'));

/**
 * Set the default locale. This controls how dates, number and currency is
 * formatted and sets the default language to use for translations.
 */
ini_set('intl.default_locale', 'es_AR');

/**
 * Register application error and exception handlers.
 */
$isCli = php_sapi_name() === 'cli';
if ($isCli) {
    (new ConsoleErrorHandler(Configure::read('Error')))->register();
} else {
    (new ErrorHandler(Configure::read('Error')))->register();
}

// Include the CLI bootstrap overrides.
if ($isCli) {
    require __DIR__ . '/bootstrap_cli.php';
}

/**
 * Set the full base URL.
 * This URL is used as the base of all absolute links.
 *
 * If you define fullBaseUrl in your config file you can remove this.
 */
if (!Configure::read('App.fullBaseUrl')) {
    $s = null;
    if (env('HTTPS')) {
        $s = 's';
    }

    $httpHost = env('HTTP_HOST');
    if (isset($httpHost)) {
        Configure::write('App.fullBaseUrl', 'http' . $s . '://' . $httpHost);
    }
    unset($httpHost, $s);
}

Cache::config(Configure::consume('Cache'));
ConnectionManager::config(Configure::consume('Datasources'));
Email::configTransport(Configure::consume('EmailTransport'));
Email::config(Configure::consume('Email'));
Log::config(Configure::consume('Log'));
Security::salt(Configure::consume('Security.salt'));

/**
 * The default crypto extension in 3.0 is OpenSSL.
 * If you are migrating from 2.x uncomment this code to
 * use a more compatible Mcrypt based implementation
 */
// Security::engine(new \Cake\Utility\Crypto\Mcrypt());

/**
 * Setup detectors for mobile and tablet.
 */
Request::addDetector('mobile', function ($request) {
    $detector = new \Detection\MobileDetect();
    return $detector->isMobile();
});
Request::addDetector('tablet', function ($request) {
    $detector = new \Detection\MobileDetect();
    return $detector->isTablet();
});

/**
 * Custom Inflector rules, can be set to correctly pluralize or singularize
 * table, model, controller names or whatever other string is passed to the
 * inflection functions.
 *
 * Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']);
 * Inflector::rules('irregular', ['red' => 'redlings']);
 * Inflector::rules('uninflected', ['dontinflectme']);
 * Inflector::rules('transliteration', ['/å/' => 'aa']);
 */
Inflector::rules('plural', ['/^(.*)$/i' => '\1']);
//Inflector::rules('singular', ['/^(.*)$/i' => '\1']);
Inflector::rules('uninflected', []);
Inflector::rules('irregular', []);

/**
 * Plugins need to be loaded manually, you can either load them one by one or all of them in a single call
 * Uncomment one of the lines below, as you need. make sure you read the documentation on Plugin to use more
 * advanced ways of loading plugins
 *
 * Plugin::loadAll(); // Loads all plugins at once
 * Plugin::load('Migrations'); //Loads a single plugin named Migrations
 *
 */

Plugin::load('Migrations');

// Only try to load DebugKit in development mode
// Debug Kit should not be installed on a production system
if (Configure::read('debug')) {
    Plugin::load('DebugKit', ['bootstrap' => true]);
}

/**
 * Connect middleware/dispatcher filters.
 */
DispatcherFactory::add('Asset');
DispatcherFactory::add('Routing');
DispatcherFactory::add('ControllerFactory');

/**
 * Enable default locale format parsing.
 * This is needed for matching the auto-localized string output of Time() class when parsing dates.
 */
Type::build('date')->useLocaleParser();
Type::build('datetime')->useLocaleParser();
<? // Path File: \App\src\Templates\Layout\default.ctp ?>
<?/** @var $this \Cake\View\View */?>
<!doctype html>
<!--[if lt IE 7]>
<html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="es_AR"> <![endif]-->
<!--[if IE 7]>
<html class="no-js lt-ie9 lt-ie8" lang="es_AR"> <![endif]-->
<!--[if IE 8]>
<html class="no-js lt-ie9" lang="es_AR"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js" lang="es_AR"> <!--<![endif]-->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title><?= $this->fetch('title') ?></title>
    <meta name="description" content="">
    <?= $this->fetch('meta') ?>
    <link rel="stylesheet" href="/css/bootstrap/bootstrap.min.css">
    <style>
        body {
            padding-top: 50px;
            padding-bottom: 20px;
        }
    </style>
    <link rel="stylesheet" href="/css/bootstrap/bootstrap-theme.min.css">
    <link rel="stylesheet" href="/css/main.css">
    <?= $this->fetch('css') ?>
    <script src="/js/vendor/modernizr-2.8.3-respond-1.4.2.min.js"></script>
    <?= $this->fetch('script') ?>
    <?= $this->Html->meta('icon') ?>
    <link rel="apple-touch-icon" href="/apple-touch-icon.png">
</head>
<body>
<!--[if lt IE 8]>
<p class="browserupgrade">Está utilizando un <strong> version obsoleta </strong> del navegador. Por favor, <a
    href="http://browsehappy.com/"> actualise su </a> navegador, para mejorar su experiencia.</p>
<![endif]-->

<div class="container">
    <?= $this->Flash->render('flash') ?>
    <?= $this->fetch('content') ?>
    <hr>
    <footer>
        <p class="text-right">&copy; EOM SYS 2003-<?= date('Y') ?></p>
    </footer>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/js/vendor/jquery-1.11.2.min.js"><\/script>')</script>
<script src="/js/vendor/bootstrap.min.js"></script>
<script src="/js/plugins.js"></script>
<script src="/js/main.js"></script>

</body>
</html>
<?
// Path File: \App\src\Templates\Usuarios\login.ctp
/** @var $this \Cake\View\View */
?>
<div class="container alto-minimo-contenido">
    <div id="loginbox" style="margin-top:50px;" class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <div class="panel-title"><i class="glyphicon glyphicon-user"></i> &nbsp;Formulario de ingreso al sistema</div>
            </div>
            <div style="padding-top:30px" class="panel-body">
                <div style="display:none" id="login-alert" class="alert alert-danger col-sm-12"></div>
                <?= $this->Flash->render('auth') ?>
                <form id="loginform" class="form-horizontal" role="form" method="post" action="<?php echo $this->Url->build(["controller" => "Usuarios", "action" => "login"]) ?>">
                    <label for="login-username">E-Mail</label>
                    <div style="margin-bottom: 25px" class="input-group">
                        <span class="input-group-addon">@</span>
                        <input id="login-username" type="text" class="form-control" name="email" value="" placeholder="E-Mail">
                    </div>
                    <label for="login-password">Clave</label>
                    <div style="margin-bottom: 25px" class="input-group">
                        <span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
                        <input id="login-password" type="password" class="form-control" name="password" placeholder="Clave">
                    </div>
                    <div style="margin-top:10px" class="form-group">
                        <div class="col-sm-push-1 col-sm-10 col-sm-pull-1 controls">
                            <button type="submit" id="btn-login" class="btn btn-primary btn-block"><i class="glyphicon glyphicon-log-in"></i> ENTRAR</button>
                        </div>
                    </div>
                </form>

            </div>
        </div>
    </div>
</div>
CREATE TABLE `usuarios` (
`id` int(12) UNSIGNED NOT NULL AUTO_INCREMENT,
`email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`nombre` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`perfiles_id` int(12) UNSIGNED NOT NULL,
`habilitado` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '0=false 1=true',
`created` datetime NULL DEFAULT NULL,
`modified` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `email` (`email`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=3
;
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.