CakePHP

Auth Component: pieno controllo dell’autenticazione

Come visto negli appunti precedenti, con la semplice attivazione il componente ha dei comportamenti predefiniti durante la fase di autenticazione che potrebbe essere necessario cambiare.
Inoltre vengono messi a disposizione metodi e proprietà utili, oltre che per la gestione dei permessi, in varie occasioni.

Personalizzare l’autenticazione
Impostando alcuni attributi, si determinano i comportamenti del componente.
Di seguito riporto la descrizione di alcune impostazioni di interesse legate all’autenticazione.

$autoRedirect = true
Determina se dopo l’avvenuta autenticazione AuthComponent redireziona automaticamente alla pagina cercata o no.

$fields = array('username' => 'username', 'password' => 'password')
Permette di specificare il nome dei campi usati nell’$userModel diversi da quelli di default necessari all’identificazione.
Ad esempio: array(‘username’ => ‘login_name’, ‘password’ => ‘passwd’).

$loginError = null
Si può impostare il messaggio di errore che viene utilizzato per comunicare la mancata corrispondenza di userid e password.
Questo è l’unico messaggio che viene visualizzato per una questione di sicurezza, così non vengono fornite indicazioni circa l’errore di autenticazione occorso.
Il messaggio qui impostato non viene visualizzato automaticamente nella session Flash, ma deve essere impostato nella view nel seguente modo
if ($session->check(‘Message.auth’)) $session->flash(‘auth’);

$authError = null
Messaggio di errore che appare quando si tenta di accedere a un’area per la quale è richiesta l’autenticazione.

$loginRedirect = null
Quando si richiede l’accesso a una pagina e si viene ridirezionati alla pagina di login $loginAction, dopo l’autenticazione l’utente viene rimandato alla pagina inizialmente richiesta, senza doverla richiedere nuovamente dopo il login.
Se $loginAction non è impostata, l’utente viene ridirezionato alla pagina specificata in $loginRedirect

$logoutRedirect = null
L’azione di defautl alla quale viene ridiretto l’utente dopo il logout. L’URL del redirect viene fornita da AuthComponent::logout() che di default restituisce $loginAction.

$loginAction = null
E’ una stringa o un array che definisce l’URL per la richiesta di login.
Se non impostata, AuthComponent la imposta come (pluralize) $userModel / login , a meno che non venga impostata $loginRedirect

$userModel = 'User'
Il nome del Model utilizzato per eseguire l’autenticazione degli utenti, ovvero l’oggetto che detiene username e password degli utenti. Come visto, il valore di default è il model User.

$userScope = array()
Si possono aggiungere delle condizioni aggiuntive per la verifica dell’utente.
Ad esempio se si vuole permettere l’accesso ai soli utenti che abbiano il campo “attivato” abilitato allora si può definire array(‘User.attivato’ => 1). In questo modo, anche se l’utente inserisce la giusta userid e password gli verrà permesso l’accesso solo se il suo stato è “attivato”.

$sessionKey = null
Definisce il nome della chiave di sessione che contiene i dati sull’utente autenticato.
Se non è specificato il valore di default è “Auth.{$userModel}”.

Queste impostazioni devono essere definite nel controller

function beforeFilter(){
		$this->Auth->authError = "Per accedere alla pagina richiesta bisogna autenticarsi";
		$this->Auth->loginError = 'Tentativo di accesso fallito. Controllare username e password.';
	}

Auth Component: creare un utente, eseguire il login e il logout

Una volta attivato il component, creata la tabella degli utenti e la maschera per il login, è il momento di creare un utente per verificare il corretto funzionamento dell’autenticazione. Ecco due modi di farlo.

Inserimento manuale nella tabella users

Posso inserire i dati nella tabella attraverso il client MySQL o un altro tool grafico (io uso phpMyAdmin ) tenendo presente che la password deve essere converita con la funzione hash utilizzata da Cake, la seguente:

hashpwd = sha1 ( salt + plain_password )

dove salt è la stringa di sicurezza configurata durante l’installazione di CakePHP presente nel file \app\config\core.php.
sha1 è l’algoritmo utilizzato per default da Cake per la funzione hash, ma è possibile cambiare metodo con MD5 e SHA256

Gestione utenti

Creo una gestione completa degli utenti sulla tabella users usando il bake da riga di comando così da poter inserire il primo utente.
A questo punto, seguendo il naming canonico, accedo alla mia gestione degli utenti http://mywebspace/users/ e…. vengo rimandato alla pagina di login !!
Per accedere, almeno temporaneamente alle azioni di users, si interviene su app_controller.php, dove avevamo attivato $authComponent e si inserisce


function beforeFilter()	{
     $this->Auth->allow();
}

A questo punto cake concede l’accesso a qualsiasi azione e possiamo andare a creare l’utente che ci interessa.
Finito l’inserimento, rimuoviamo la riga con il metodo allow() e proviamo a d accedere di nuovo a http://mywebspace/users/.
Ci dovrebbe chiedere nuovamente la userdi – password, ma questa volta si può inserire l’utente inserito e accedere alla sezione della gestione utenti.

Da notare due cose: quando inserisce ad esempio l’URL http://mywebspace/articles/view/234 l’utente viene rimandato a http://mywebspace/users/login e dopo aver effettuato il login, viene reindirizzato sull’URL da lui richiesta in precedenza.
L’altra cosa da notare è che sulla password immessa in fase di inserimento utente, viene automaticamente eseguito l’hash prima del salvataggio in tabella, senza bisogno di aggiungere alcun codice nel controller, nel model o altrove.

Il log out
Per chiudere la sessione è sufficente creare un’action nel controller nel quale invocare il metodo logout di Auth

function logout(){
		$this->Session->setFlash("L\'utente è uscito regolarmente!");
		$this->redirect($this->Auth->logout());
	}

Auth Component: attivazione e quickstart

Attivazione del component Auth

Ho appena installato CakePHP e configurato il database completamente vuoto.
Mi appare la pagina iniziale di Cake, con le indicazioni canoniche.

Creo il file /app/app_controller.php e dichiaro come componente Auth

class AppController extends Controller {
	var $components = array('Auth');
}

Il component è subito attivato e ricaricando la pagina si ottiene un errore perchè si è stati reindirizzati all’URL /users/login.
In pratica essendo attivato in ogni pagina richiesta, il sistema riconosce che l’utente non è autenticato e considera necessaria l’autenticazione per accedere a qualsiasi pagina del sito.
Quindi avviene il reindirizzamento verso l’indirizzo di default /users/login per far si che l’utente si possa autenticare.

Chiaramente si riceve un messaggio di errore se la pagina ancora non esiste, ovvero il controller e la view relativa.

La struttura necessaria

Servono quindi:

  • un model che permetta di conservare e confrontare i dati dell’utente per l’accesso (userid e password)
  • un controller per le azioni relative all’utente (login, logout)
  • una view per il form di autenticazione

La tabella

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(5) unsigned NOT NULL,
  `username` varchar(30) NOT NULL,
  `password` varchar(30) NOT NULL,
  `nome` varchar(60) NOT NULL,
  `cognome` varchar(100) NOT NULL,
  `active` tinyint(1) unsigned NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Gli unici campi essenziali sono username e password. Si possono utilizzare anche nomi diversi per questi campi, purchè si impostino correttamente nel component (vedi sotto).

Il model

class User extends AppModel {
	var $name = 'User';
}

Il controller

class UsersController extends AppController {

	var $name = 'Users';
	var $helpers = array('Html', 'Form' );

	function login(){
	}
}

Il view (login.ctp)


Login

create('User', array('action' => 'login'));?> input('username');?> input('password');?> submit('Login');?> end(); ?>

A questo punto, accedendo a qualsiasi pagina dell’applicazione, Cake intercetta la richiesta e prima di soddisfarla, controlla che l’utente sia autenticato.
In caso negativo esegue un redirect sulla pagina di login.

AJAX semplice semplice

Si vuole creare una pagina dove è presente un link e un DIV con funzioni da contenitore.
Si vuole che alla selezione del link il DIV venga popolato con una lista di elementi, ricavati da un database, sfruttando AJAX, quindi senza ricaricare l’intera pagina.

Requisiti necessari

Sul server devono essere installate le librerie Javascript prototype e scriptaculous e devono essere richiamate nella pagina. Inoltre il charset deve essere impostato a “UTF-8″.

Il controller

Nel controller devono essere richiamati gli helper necessari: Html, Javascript e Ajax.
Richiamando anche il component “RequestHandler” si potranno sfruttare funzionalità circa il rendering di layouts e template.
Il metodo index() imposterà la variabile che sarà stampata inizialmente nel contenitore.
Il metodo listcat() (o qualsivoglia nome) ricaverà la lista da stampare (in questo caso una lista di categorie).

Le view

index.ctp conterrà l’intera pagina corredata dei vari elementi necessari quali il titolo o menu, ecc. oltre al necessario contenitore DIV che verrà aggiornato con l’inserimento dei dati ricavati.

listcat.ctp invece conterrà la sola lista da stampare poichè rappresenta il contenuto che verrà inserito nel contenitore DIV.

Quindi è necessario ricordare che il sistema aggiornerà l’oggetto DOM con ciò che viene prodotto dalla view che l’action richiamata (listcat) ha prodotto.

Di seguito il codice di esempio.

Controller

set("lista","Vuoto");
    }

    function listacat(){
$this->set('lista',$this->Category->findAll());
    }
}
?>

Da aggiungere al Layout
charsetTag('UTF-8') ?>
link('prototype') ?>
link('scriptaculous.js?load=effects') ?>

View – listacat.ctp

    {$cat['Category']['nome']}
    
    ";
    }
    ?>

View - index.ctp


Tests

link("Prova AJAX", "/tests/listacat",array("update" => "content")); ?>

Behaviors

I behaviors stanno al model come i components stanno al controller.

Un esempio: abbiamo un behavior chiamto List.
Vogliamo creare una classe ListBehavior che estende ModelBehavior, e viene salvata in app/models/behaviors.
Puoi includere questo bhavior nel tuo modello nel seguente modo

class Post extends Model {
    var $actsAs = array("List");
}

E questo è quanto.
A questo punto ogni metodo definito in ListBehavior sarà disponibile come nativo in Post.
Per esempio se vuene definito un metodo chiamato ListBehavior::moveFirst( ), si potrà accedervi nel seguente modo $this->Post->moveFirst( );

HABTM: l’associazione HasAndBelongToMany

Questa associazione gestisce la classica relazione “molti-a-molti”, tipo quella che si può creare tra due oggetti quali “Articles” e “Keywords”.
Ogni articolo può avere associate più parole chiave e una parola chiave può essere associata a più articoli.

Supponiamo di disporre già delle tabelle articles e keywords.
Sarà necessario creare la tabella di join che permetta la relazione “molti-a-molti” tra le due tabelle.
Per convenzione in CakePHP questa tabella assumerà un nome che viene ricavato dai nomi delle due tabelle implicate ordinati alfabeticamente, separate da un carattere underscore ( _ ).

Nel nostro caso la join table verrà chiamata articles_keywords e avrà come unici campi le due chiavi esterne che fanno riferimento alla convenzione CakePHP: “article_id” e “keyword_id” che formeranno la chiave primaria della tabella.

Il passo successivo è quello di definire l’associazione nel model relativo agli articoli

var $hasAndBelongsToMany = array('Keyword' =>
                 array('className'    => 'Keyword',
                          'joinTable'    => 'articles_keywords',
                          'foreignKey'   => 'article_id',
                          'associationForeignKey'=> 'keyword_id',
                          'conditions'   => '',
                          'order'        => 'Keyword.keyword ASC',
                          'limit'        => '',
                          'unique'       => true,
                          'finderQuery'  => '',
                          'deleteQuery'  => '',
                  );

‘joinTable’ è la tabella di join che abbiamo creato e ‘foreignKey’ e ‘associationForeignKey’ rappresentano i campi relative alle chiavi.
Secondo il manuale di CakePHP non sarebbe necessario specificare il nome dei campi se si rispettano le convenzioni.

Salvare i dati

Poniamo il caso che abbiamo un articolo già inserito nella tabella Articles (ma se non è ancora presente fa lo stesso) al quale vogliamo associare delle keyword da prelevare dalla tabella Keywords.

La procedura da seguire è semplicemente la seguente:

1. Si popola l’array $this->data con i valori da inserire.

L’array deve contenere i dati dell’articolo e quelli delle keyword da associare all’articolo. In particolare i dati relativi alla tabella associata devono avere un particolare formato.
Quindi immaginiamo di avere $this->data già popolato da un form compilato e inviato dall’utente che appare come segue:

Array
(
    [Article] => Array
        (
            [id] => 4
            [category_id] => 12
            [titolo] => Questo è il titolo dell'articolo
            [sottotitolo] => Sottotitolo
            [testo] => Bla bla bla bla bla bla bla bla bla bla bla
            [user_id] => 0
            [created] => 2007-03-02 08:16:44
            [modified] => 2007-03-06 14:00:45
            [data_pub] =>
            [data_fine_pub] =>
            [onindex] => 0
            [pubblicato] => 0
        )

    [Category] => Array
        (
            [descrizione] => Novità
        )

    [Keyword] => Array
        (
            [0] => Array
                (
                    [id] => 2
                    [keyword] => aids
                )

            [1] => Array
                (
                    [id] => 9
                    [keyword] => bambini
                )

            [2] => Array
                (
                    [id] => 8
                    [keyword] => fame
                )

            [3] => Array
                (
                    [id] => 4
                    [keyword] => popolazioni
                )

        )

)

A questo punto bisogna impostare l’elemento

$this->data['Keyword'] = array('Keyword' => array ( 23, 56, 67));

dove 23, 56 e 67 rappresentano l’id delle keyword da associare.
Volendo riscrivere l’array nel formato indicato dal manuale si può dire che l’array avrà un formato del tipo

['Keyword']['Keyword'][23]
['Keyword']['Keyword'][56]
['Keyword']['Keyword'][67]

2. Si invoca il metodo save del modello Article.

In questo modo si può salvare l’intero articolo e associare le keyword selezionate.

Per un passaggio più stringato basta impostare il seguente array
$data = array (‘Article’ => array(‘id’ => 4) ,
‘Keyword’ => array(‘Keyword’ => array (23,56,67))
)

Questo è sufficente per eseguire la sola associazione delle keyword

Quando le cose si fanno complicate
Sicuramente capiterà di dover gestire la relazione many-to-many impostando delle informazioni aggiuntive circa l’associazione, tipo la data di creazione dell’associazione (ad esempio l’associazione Users-Groups).
Per questa problematica è utilissimo leggere un post di ThinkingPHP and beyond che tratta l’argomento nei particolari:

Modeling relationships in CakePHP (faking Rails ThroughAssociation)

Un altro articolo interessante:

ThroughAssociations

Associazioni: chiavi da impostare

Un elenco delle chiavi che si possono utilizzare nell’impostazione delle associazioni che ho recuperato sbirciando il codice:

belongsTo
  - className
  - foreignKey
  - conditions
  - fields
  - order
  - counterCache

hasOne
  - className
  - foreignKey
  - conditions
  - fields
  - order
  - dependent

hasMany
  - className
  - foreignKey
  - conditions
  - fields
  - order
  - limit
  - offset
  - dependent
  - exclusive
  - finderQuery
  - counterQuery

hasAndBelongsToMany
  - className
  - joinTable
  - foreignKey
  - associationForeignKey
  - conditions
  - fields
  - order
  - limit
  - offset
  - unique
  - finderQuery
  - deleteQuery
  - insertQuery

Riferimento API
Riferimento manuale

La gestione delle relazioni tra tabelle: le associazioni

Una delle più potenti funzioni offerte da CakePHP è la gestione delle relazioni fornita dai modelli. Ci sono quattro tipi di associazioni che si possono impiegare:

  • hasOne
  • hasMany
  • belongsTo
  • hasAndBelongsToMany

Si prenda come esmpio un sitema di gestione utenti di un blog.

hasOne
rappresenta la relazione uno-a-uno e potrebbe essere utilizzato per associare un profilo ad ogni utente.

hasMany
relazione uno-a-molti, nel nostro blog potrebbe rappresentare la relazione tra gli utenti e i commenti che hanno inserito

belongsTo
rappresenta il legame tra un modello collegato con quello “collegante”. Ad esempio ogni commento belongsTo utente

hasAndBelongsToMany
relazione molti-a-molti; un commento potrebbe essere associato a più categorie e una categoria richiamata da più commenti.

Riferimento al manuale
http://manual.cakephp.org/chapter/models#association

Un tutorial dall’IBM

Un tutorial in cinque parti che tocca alcuni degli aspetti più interessanti di CakePHP: gestione dei permessi, cache, ecc.
Occorre la registrazione (gratuita)
- IBM developersWork

Validazione dei form in 1.2

Per la validazione dei form c’è un utilissimo tutorial nel Bakery.

Un particolare che si poteva aggiungere (ma che si trova nel gruppo di discussione) è la possibilità di specidicare se un campo possa rimanere vuoto anche in presenza della regola di validazione che viene applicata solo se è inserito un dato.

Qui sotto l’esempio per consentire questa opzione.

var $validate = array(
   'url' => array(
        'allowEmpty' => true,
        'rule' => 'url','message' => 'Il formato del link non è valido. (es.: http://www.nomesito.it/altro.html)'
    )
);

« PrecedenteSuccessiva »