Guia de Início Rápido

A melhor forma de viver experiências e aprender sobre CakePHP é sentar e construir algo. Para começar nós iremos construir uma aplicação simples de blog.

Tutorial - Criando um Bookmarker - Parte 1

Esse tutorial vai guiar você através da criação de uma simples aplicação de marcação (bookmarker). Para começar, nós vamos instalar o CakePHP, criar nosso banco de dados, e usar as ferramentas que o CakePHP fornece para subir nossa aplicação de forma rápida.

Aqui está o que você vai precisar:

  1. Um servidor de banco de dados. Nós vamos usar o servidor MySQL neste tutorial. Você precisa saber o suficiente sobre SQL para criar um banco de dados: O CakePHP vai tomar as rédeas a partir daí. Por nós estarmos usando o MySQL, também certifique-se que você tem a extensão pdo_mysql habilitada no PHP.

  2. Conhecimento básico sobre PHP.

Vamos começar!

Instalação do CakePHP

A maneira mais fácil de instalar o CakePHP é usando Composer, um gerenciador de dependências para o PHP. É uma forma simples de instalar o CakePHP a partir de seu terminal ou prompt de comando. Primeiro, você precisa baixar e instalar o Composer. Se você tiver instalada a extensão cURL do PHP, execute o seguinte comando:

curl -s https://getcomposer.org/installer | php

Ao invés disso, você também pode baixar o arquivo composer.phar do site oficial.

Em seguida, basta digitar a seguinte linha no seu terminal a partir do diretório onde se localiza o arquivo composer.phar para instalar o esqueleto de aplicações do CakePHP no diretório bookmarker.

php composer.phar create-project --prefer-dist cakephp/app:4.* bookmarker

A vantagem de usar Composer é que ele irá completar automaticamente um conjunto importante de tarefas, como configurar as permissões de arquivo e criar a sua config/app.php.

Há outras maneiras de instalar o CakePHP. Se você não puder ou não quiser usar Composer, veja a seção Instalação.

Independentemente de como você baixou o CakePHP, uma vez que sua instalação for concluída, a estrutura dos diretórios deve ficar parecida com o seguinte:

/bookmarker
    /bin
    /config
    /logs
    /plugins
    /src
    /tests
    /tmp
    /vendor
    /webroot
    .editorconfig
    .gitignore
    .htaccess
    .travis.yml
    composer.json
    index.php
    phpunit.xml.dist
    README.md

Agora pode ser um bom momento para aprender sobre como a estrutura de diretórios do CakePHP funciona: Confira a seção Estrutura de pastas do CakePHP.

Verificando nossa instalação

Podemos checar rapidamente que a nossa instalação está correta, verificando a página inicial padrão. Antes que você possa fazer isso, você vai precisar iniciar o servidor de desenvolvimento:

bin/cake server

Isto irá iniciar o servidor embutido do PHP na porta 8765. Abra http://localhost:8765 em seu navegador para ver a página de boas-vindas. Todas as verificações devem estar checadas corretamente, a não ser a conexão com banco de dados do CakePHP. Se não, você pode precisar instalar extensões do PHP adicionais, ou definir permissões de diretório.

Criando o banco de dados

Em seguida, vamos criar o banco de dados para a nossa aplicação. Se você ainda não tiver feito isso, crie um banco de dados vazio para uso nesse tutorial, com um nome de sua escolha, por exemplo, cake_bookmarks. Você pode executar o seguinte SQL para criar as tabelas necessárias:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE bookmarks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(50),
    description TEXT,
    url TEXT,
    created DATETIME,
    modified DATETIME,
    FOREIGN KEY user_key (user_id) REFERENCES users(id)
);

CREATE TABLE tags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (title)
);

CREATE TABLE bookmarks_tags (
    bookmark_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (bookmark_id, tag_id),
    INDEX tag_idx (tag_id, bookmark_id),
    FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
    FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);

Você deve ter notado que a tabela bookmarks_tags utilizada uma chave primária composta. O CakePHP suporta chaves primárias compostas em quase todos os lugares, tornando mais fácil construir aplicações multi-arrendados.

Os nomes de tabelas e colunas que usamos não foram arbitrárias. Usando convenções de nomenclatura do CakePHP, podemos alavancar o desenvolvimento e evitar ter de configurar o framework. O CakePHP é flexível o suficiente para acomodar até mesmo esquemas de banco de dados legados inconsistentes, mas aderir às convenções vai lhe poupar tempo.

Configurando o banco de dados

Em seguida, vamos dizer ao CakePHP onde o nosso banco de dados está e como se conectar a ele. Para muitos, esta será a primeira e última vez que você vai precisar configurar qualquer coisa.

A configuração é bem simples: basta alterar os valores do array Datasources.default no arquivo config/app.php pelos que se aplicam à sua configuração. A amostra completa da gama de configurações pode ser algo como o seguinte:

return [
    // Mais configuração acima.
    'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            'username' => 'cakephp',
            'password' => 'AngelF00dC4k3~',
            'database' => 'cake_bookmarks',
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
        ],
    ],
    // Mais configuração abaixo.
];

Depois de salvar o seu arquivo config/app.php, você deve notar que a mensagem “CakePHP is able to connect to the database” tem uma marca de verificação.

Nota

Uma cópia do arquivo de configuração padrão do CakePHP é encontrado em config/app.default.php.

Gerando o código base

Devido a nosso banco de dados seguir as convenções do CakePHP, podemos usar o bake console para gerar rapidamente uma aplicação básica . Em sua linha de comando execute:

bin/cake bake all users
bin/cake bake all bookmarks
bin/cake bake all tags

Isso irá gerar os controllers, models, views, seus casos de teste correspondentes, e fixtures para os nossos users, bookmarks e tags. Se você parou seu servidor, reinicie-o e vá para http://localhost:8765/bookmarks.

Você deverá ver uma aplicação que dá acesso básico, mas funcional a tabelas de banco de dados. Adicione alguns users, bookmarks e tags.

Adicionando criptografia de senha

Quando você criou seus users, você deve ter notado que as senhas foram armazenadas como texto simples. Isso é muito ruim do ponto de vista da segurança, por isso vamos consertar isso.

Este também é um bom momento para falar sobre a camada de modelo. No CakePHP, separamos os métodos que operam em uma coleção de objetos, e um único objeto em diferentes classes. Métodos que operam na recolha de entidades são colocadas na classe Table, enquanto as características pertencentes a um único registro são colocados na classe Entity.

Por exemplo, a criptografia de senha é feita no registro individual, por isso vamos implementar esse comportamento no objeto entidade. Dada a circunstância de nós querermos criptografar a senha cada vez que é definida, vamos usar um método modificador/definidor. O CakePHP vai chamar métodos de definição baseados em convenções a qualquer momento que uma propriedade é definida em uma de suas entidades. Vamos adicionar um definidor para a senha. Em src/Model/Entity/User.php adicione o seguinte:

namespace App\Model\Entity;

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

class User extends Entity
{

    // Code from bake.

    protected function _setPassword($value)
    {
        $hasher = new DefaultPasswordHasher();
        return $hasher->hash($value);
    }
}

Agora atualize um dos usuários que você criou anteriormente, se você alterar sua senha, você deve ver um senha criptografada ao invés do valor original nas páginas de lista ou visualização. O CakePHP criptografa senhas com bcrypt por padrão. Você também pode usar sha1 ou md5 caso venha a trabalhar com um banco de dados existente.

Recuperando bookmarks com uma tag específica

Agora que estamos armazenando senhas com segurança, podemos construir algumas características mais interessantes em nossa aplicação. Uma vez que você acumulou uma coleção de bookmarks, é útil ser capaz de pesquisar através deles por tag. Em seguida, vamos implementar uma rota, a ação do controller, e um método localizador para pesquisar através de bookmarks por tag.

Idealmente, nós teríamos uma URL que se parece com http://localhost:8765/bookmarks/tagged/funny/cat/gifs. Isso deveria nos permitir a encontrar todos os bookmarks que têm as tags “funny”, “cat” e “gifs”. Antes de podermos implementar isso, vamos adicionar uma nova rota. Em config/routes.php, adicione o seguinte na parte superior do arquivo:

Router::scope(
    '/bookmarks',
    ['controller' => 'Bookmarks'],
    function ($routes) {
        $routes->connect('/tagged/*', ['action' => 'tags']);
    }
);

O trecho acima define uma nova «rota» que liga o caminho /bookmarks/tagged/*, a BookmarksController::tags(). Ao definir rotas, você pode isolar como suas URLs parecerão, de como eles são implementadas. Se fôssemos visitar http://localhost:8765/bookmarks/tagged, deveriamos ver uma página de erro informativa do CakePHP. Vamos implementar esse método ausente agora. Em src/Controller/BookmarksController.php adicione o seguinte trecho:

public function tags()
{
    $tags = $this->request->getParam('pass');
    $bookmarks = $this->Bookmarks->find('tagged', [
        'tags' => $tags
    ]);
    $this->set(compact('bookmarks', 'tags'));
}

Criando o método localizador

No CakePHP nós gostamos de manter as nossas ações do controller enxutas, e colocar a maior parte da lógica de nossa aplicação nos modelos. Se você fosse visitar a URL /bookmarks/tagged agora, você veria um erro sobre o método findTagged não estar implementado ainda, então vamos fazer isso. Em src/Model/Table/BookmarksTable.php adicione o seguinte:

public function findTagged(Query $query, array $options)
{
    $bookmarks = $this->find()
        ->select(['id', 'url', 'title', 'description']);

    if (empty($options['tags'])) {
        $bookmarks
            ->leftJoinWith('Tags')
            ->where(['Tags.title IS' => null]);
    } else {
        $bookmarks
            ->innerJoinWith('Tags')
            ->where(['Tags.title IN ' => $options['tags']]);
    }

    return $bookmarks->group(['Bookmarks.id']);
}

Nós implementamos um método localizador customizado. Este é um conceito muito poderoso no CakePHP que lhe permite construir consultas reutilizáveis. Em nossa pesquisa, nós alavancamos o método matching() que nos habilita encontrar bookmarks que têm uma tag “correspondente”.

Criando a view

Agora, se você visitar a URL /bookmarks/tagged, o CakePHP irá mostrar um erro e deixá-lo saber que você ainda não fez um arquivo view. Em seguida, vamos construir o arquivo view para a nossa ação tags. Em templates/Bookmarks/tags.php coloque o seguinte conteúdo:

<h1>
    Bookmarks tagged with
    <?= $this->Text->toList(h($tags)) ?>
</h1>

<section>
<?php foreach ($bookmarks as $bookmark): ?>
    <article>
        <h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4>
        <small><?= h($bookmark->url) ?></small>
        <?= $this->Text->autoParagraph(h($bookmark->description)) ?>
    </article>
<?php endforeach; ?>
</section>

O CakePHP espera que os nossos templates sigam a convenção de nomenclatura onde o nome do template é a versão minúscula e grifada do nome da ação do controller.

Você pode perceber que fomos capazes de utilizar as variáveis $tags e bookmarks em nossa view. Quando usamos o método set() em nosso controller, automaticamente definimos variáveis específicas que devem ser enviadas para a view. A view vai tornar todas as variáveis passadas disponíveis nos templates como variáveis locais.

Em nossa view, usamos alguns dos helpers nativos do CakePHP. Helpers são usados para criar lógica re-utilizável para a formatação de dados, a criação de HTML ou outra saída da view.

Agora você deve ser capaz de visitar a URL /bookmarks/tagged/funny e ver todas os bookmarks com a tag “funny”.

Até agora, nós criamos uma aplicação básica para gerenciar bookmarks, tags e users. No entanto, todos podem ver as tags de todos os usuários. No próximo capítulo, vamos implementar a autenticação e restringir os bookmarks visíveis para somente aqueles que pertencem ao usuário atual.

Agora vá a Tutorial - Criando um Bookmarker - Parte 2 para continuar a construir sua aplicação ou mergulhe na documentação para saber mais sobre o que CakePHP pode fazer por você.

Tutorial - Criando um Bookmarker - Parte 2

Depois de terminar a primeira parte deste tutorial, você deve ter uma aplicação muito básica. Neste capítulo iremos adicionar autenticação e restringir as bookmarks para que cada usuário possa ver/modificar somente aquelas tags que possuam.

Adicionando login

No CakePHP, a autenticação é feita por Componentes. Os Components podem ser considerados como formas de criar pedaços reutilizáveis de código relacionado a controllers com uma característica específica ou conceito. Os components também podem se ligar ao evento do ciclo de vida do controller e interagir com a sua aplicação. Para começar, vamos adicionar o AuthComponent a nossa aplicação. É essencial que cada método exija autenticação, por isso vamos acrescentar o AuthComponent em nosso AppController:

// Em src/Controller/AppController.php
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
    public function initialize()
    {
        $this->loadComponent('Flash');
        $this->loadComponent('Auth', [
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'email',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => [
                'controller' => 'Users',
                'action' => 'login'
            ]
        ]);

        // Permite a ação display, assim nosso pages controller
        // continua a funcionar.
        $this->Auth->allow(['display']);
    }
}

Acabamos de dizer ao CakePHP que queremos carregar os components Flash e Auth. Além disso, temos a configuração personalizada do AuthComponent, assim a nossa tabela users pode usar email como username. Agora, se você for a qualquer URL, você vai ser chutado para /users/login, que irá mostrar uma página de erro já que não escrevemos o código ainda. Então, vamos criar a ação de login:

// Em src/Controller/UsersController.php

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->error('Your username or password is incorrect.');
    }
}

E em templates/Users/login.php adicione o seguinte trecho:

<h1>Login</h1>
<?= $this->Form->create() ?>
<?= $this->Form->input('email') ?>
<?= $this->Form->input('password') ?>
<?= $this->Form->button('Login') ?>
<?= $this->Form->end() ?>

Agora que temos um formulário de login simples, devemos ser capazes de efetuar login com um dos users que tenham senha criptografada.

Nota

Se nenhum de seus users tem senha criptografada, comente a linha loadComponent('Auth'). Então vá e edite o user, salvando uma nova senha para ele.

Agora você deve ser capaz de entrar. Se não, certifique-se que você está usando um user que tenha senha criptografada.

Adicionando logout

Agora que as pessoas podem efetuar o login, você provavelmente vai querer fornecer uma maneira de encerrar a sessão também. Mais uma vez, no UsersController, adicione o seguinte código:

public function logout()
{
    $this->Flash->success('You are now logged out.');
    return $this->redirect($this->Auth->logout());
}

Agora você pode visitar /users/logout para sair e ser enviado à página de login.

Ativando inscrições

Se você não estiver logado e tentar visitar / usuários / adicionar você vai ser expulso para a página de login. Devemos corrigir isso se quisermos que as pessoas se inscrevam em nossa aplicação. No UsersController adicione o seguinte trecho:

public function beforeFilter(\Cake\Event\Event $event)
{
    $this->Auth->allow(['add']);
}

O texto acima diz ao AuthComponent que a ação add não requer autenticação ou autorização. Você pode querer dedicar algum tempo para limpar a /users/add e remover os links enganosos, ou continuar para a próxima seção. Nós não estaremos construindo a edição do usuário, visualização ou listagem neste tutorial, então eles não vão funcionar, já que o AuthComponent vai negar-lhe acesso a essas ações do controller.

Restringindo acesso

Agora que os usuários podem se conectar, nós vamos querer limitar os bookmarks que podem ver para aqueles que fizeram. Nós vamos fazer isso usando um adaptador de “autorização”. Sendo os nossos requisitos bastante simples, podemos escrever um código em nossa BookmarksController. Mas antes de fazer isso, vamos querer dizer ao AuthComponent como nossa aplicação vai autorizar ações. Em seu AppController adicione o seguinte:

public function isAuthorized($user)
{
    return false;
}

Além disso, adicione o seguinte à configuração para Auth em seu AppController:

'authorize' => 'Controller',

Seu método initialize agora deve parecer com:

public function initialize()
{
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
        'authorize'=> 'Controller',//adicionado essa linha
        'authenticate' => [
            'Form' => [
                'fields' => [
                    'username' => 'email',
                    'password' => 'password'
                ]
            ]
        ],
        'loginAction' => [
            'controller' => 'Users',
            'action' => 'login'
        ],
        'unauthorizedRedirect' => $this->referer()
    ]);

    // Permite a ação display, assim nosso pages controller
    // continua a funcionar.
    $this->Auth->allow(['display']);
}

Vamos usar como padrão, negação do acesso, e de forma incremental conceder acesso onde faça sentido. Primeiro, vamos adicionar a lógica de autorização para os bookmarks. Em seu BookmarksController adicione o seguinte:

public function isAuthorized($user)
{
    $action = $this->request->params['action'];

    // As ações add e index são permitidas sempre.
    if (in_array($action, ['index', 'add', 'tags'])) {
        return true;
    }
    // Todas as outras ações requerem um id.
    if (!$this->request->getParam('pass.0')) {
        return false;
    }

    // Checa se o bookmark pertence ao user atual.
    $id = $this->request->getParam('pass.0');
    $bookmark = $this->Bookmarks->get($id);
    if ($bookmark->user_id == $user['id']) {
        return true;
    }
    return parent::isAuthorized($user);
}

Agora, se você tentar visualizar, editar ou excluir um bookmark que não pertença a você, você deve ser redirecionado para a página de onde veio. No entanto, não há nenhuma mensagem de erro sendo exibida, então vamos corrigir isso a seguir:

// In templates/layout/default.php
// Under the existing flash message.
<?= $this->Flash->render('auth') ?>

Agora você deve ver as mensagens de erro de autorização.

Corrigindo a view de listagem e formulários

Enquanto view e delete estão trabalhando, edit, add e index tem alguns problemas:

  1. Ao adicionar um bookmark, você pode escolher o user.

  2. Ao editar um bookmark, você pode escolher o user.

  3. A página de listagem mostra os bookmarks de outros users.

Primeiramente, vamos refatorar o formulário de adição. Para começar remova o input('user_id') a partir de templates/Bookmarks/add.php. Com isso removido, nós também vamos atualizar o método add:

public function add()
{
    $bookmark = $this->Bookmarks->newEntity();
    if ($this->request->is('post')) {
        $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData());
        $bookmark->user_id = $this->Auth->user('id');
        if ($this->Bookmarks->save($bookmark)) {
            $this->Flash->success('The bookmark has been saved.');
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error('The bookmark could not be saved. Please, try again.');
    }
    $tags = $this->Bookmarks->Tags->find('list');
    $this->set(compact('bookmark', 'tags'));
}

Ao definir a propriedade da entidade com os dados da sessão, nós removemos qualquer possibilidade do user modificar algo que não pertenca a ele. Nós vamos fazer o mesmo para o formulário edit e action edit. Sua ação edit deve ficar assim:

public function edit($id = null)
{
    $bookmark = $this->Bookmarks->get($id, [
        'contain' => ['Tags']
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData());
        $bookmark->user_id = $this->Auth->user('id');
        if ($this->Bookmarks->save($bookmark)) {
            $this->Flash->success('The bookmark has been saved.');
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error('The bookmark could not be saved. Please, try again.');
    }
    $tags = $this->Bookmarks->Tags->find('list');
    $this->set(compact('bookmark', 'tags'));
}

View de listagem

Agora, nós precisamos apenas exibir bookmarks para o user logado. Nós podemos fazer isso ao atualizar a chamada para paginate(). Altere sua ação index:

public function index()
{
    $this->paginate = [
        'conditions' => [
            'Bookmarks.user_id' => $this->Auth->user('id'),
        ]
    ];
    $this->set('bookmarks', $this->paginate($this->Bookmarks));
}

Nós também devemos atualizar a action tags() e o método localizador relacionado, mas vamos deixar isso como um exercício para que você conclua por sí.

Melhorando a experiência com as tags

Agora, adicionar novas tags é um processo difícil, pois o TagsController proíbe todos os acessos. Em vez de permitir o acesso, podemos melhorar a interface do usuário para selecionar tags usando um campo de texto separado por vírgulas. Isso permitirá dar uma melhor experiência para os nossos usuários, e usar mais alguns grandes recursos no ORM.

Adicionando um campo computado

Porque nós queremos uma maneira simples de acessar as tags formatados para uma entidade, podemos adicionar um campo virtual/computado para a entidade. Em src/Model/Entity/Bookmark.php adicione o seguinte:

use Cake\Collection\Collection;

protected function _getTagString()
{
    if (isset($this->_fields['tag_string'])) {
        return $this->_fields['tag_string'];
    }
    if (empty($this->tags)) {
        return '';
    }
    $tags = new Collection($this->tags);
    $str = $tags->reduce(function ($string, $tag) {
        return $string . $tag->title . ', ';
    }, '');
    return trim($str, ', ');
}

Isso vai nos deixar acessar a propriedade computada $bookmark->tag_string. Vamos usar essa propriedade em inputs mais tarde. Lembre-se de adicionar a propriedade tag_string a lista _accessible em sua entidade.

Em src/Model/Entity/Bookmark.php adicione o tag_string ao _accessible desta forma:

protected array $_accessible = [
    'user_id' => true,
    'title' => true,
    'description' => true,
    'url' => true,
    'user' => true,
    'tags' => true,
    'tag_string' => true,
];

Atualizando as views

Com a entidade atualizado, podemos adicionar uma nova entrada para as nossas tags. Nas views add e edit, substitua tags._ids pelo seguinte:

<?= $this->Form->input('tag_string', ['type' => 'text']) ?>

Persistindo a string tag

Agora que podemos ver as tags como uma string existente, vamos querer salvar os dados também. Por marcar o tag_string como acessível, o ORM irá copiar os dados do pedido em nossa entidade. Podemos usar um método beforeSave para analisar a cadeia tag e encontrar/construir as entidades relacionadas. Adicione o seguinte em src/Model/Table/BookmarksTable.php:

public function beforeSave($event, $entity, $options)
{
    if ($entity->tag_string) {
        $entity->tags = $this->_buildTags($entity->tag_string);
    }
}

protected function _buildTags($tagString)
{
    $new = array_unique(array_map('trim', explode(',', $tagString)));
    $out = [];
    $query = $this->Tags->find()
        ->where(['Tags.title IN' => $new]);

    // Remove tags existentes da lista de novas tags.
    foreach ($query->extract('title') as $existing) {
        $index = array_search($existing, $new);
        if ($index !== false) {
            unset($new[$index]);
        }
    }
    // Adiciona tags existentes.
    foreach ($query as $tag) {
        $out[] = $tag;
    }
    // Adiciona novas tags.
    foreach ($new as $tag) {
        $out[] = $this->Tags->newEntity(['title' => $tag]);
    }
    return $out;
}

Embora esse código seja um pouco mais complicado do que o que temos feito até agora, ele ajuda a mostrar o quão poderosa a ORM do CakePHP é. Você pode facilmente manipular resultados da consulta usando os métodos de Coleções, e lidar com situações em que você está criando entidades sob demanda com facilidade.

Terminando

Nós expandimos nossa aplicação bookmarker para lidar com situações de autenticação e controle de autorização/acesso básico. Nós também adicionamos algumas melhorias agradáveis à UX, aproveitando os recursos FormHelper e ORM.

Obrigado por dispor do seu tempo para explorar o CakePHP. Em seguida, você pode saber mais sobre o Models (Modelos), ou você pode ler os Utilizando o CakePHP.