CakePHP3ブログチュートリアル

Standard

https://book.cakephp.org/3.0/_downloads/en/CakePHPCookbook.pdfにもとづいて

{
    "_comment": "vscode settings.json",
    "php.validate.executablePath": "C:\\xampp\\php\\php.exe",
    "php.executablePath": "C:\\xampp\\php\\php.exe",
    "php.suggest.basic": false,
    "workbench.iconTheme": "vscode-icons"
}
; php.ini
extension=intl
[xdebug]
zend_extension=php_xdebug
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_host=localhost
xdebug.remote_port=9000


$ composer self-update && composer create-project --prefer-dist cakephp/app blog
$ cd blog
$ code .

// config/app.php
// ...
    'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
// ...
# https://book.cakephp.org/3.0/en/migrations.html
# https://www.ritolab.com/entry/61
# https://www.ritolab.com/entry/62
$ bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified
$ bin/cake migrations migrate
$ bin/cake bake seed Articles

    // config/Seeds/ArticlesSeed.php
    // ....
    public function run()
    {
        $datetime = date('Y-m-d H:i:s');
        $data = [
            [
                'title' => 'The Title1',
                'body' => 'This is the article body1.',
                'created' => $datetime,
                'modified' => $datetime,
            ],
            [
                'title' => 'The Title2',
                'body' => 'This is the article body2.',
                'created' => $datetime,
                'modified' => $datetime,
            ],
        ];
 
        $table = $this->table('articles');
        $table->insert($data)->save();
    }
    // ...
 
$ bin/cake migrations seed


$ bin/cake bake model Articles
$ bin/cake bake controller Articles
$ bin/cake bake template Articles
    // config/routes.php
    //$routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
    // comment out or remove the line that defines the default root route, and add:
    $routes->connect('/', ['controller' => 'Articles', 'action' => 'index']);

<?php
// src/Controller/ArticlesController.php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Http\Exception\NotFoundException;
class ArticlesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Flash');
    }
    public function index()
    {
        $articles = $this->paginate($this->Articles);
        $this->set(compact('articles'));
    }
    public function view($id = null)
    {
        $article = $this->Articles->get($id, [
            'contain' => [],
        ]);
        $this->set('article', $article);
    }
    public function add()
    {
        $article = $this->Articles->newEntity();
        if ($this->request->is('post')) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            if ($this->Articles->save($article)) {
                $this->Flash->success(__('The article has been saved.'));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The article could not be saved. Please, try again.'));
        }
        $this->set(compact('article'));
        // Just added the categories list to be able to choose
        // one category for an article
        $categories = $this->Articles->Categories->find('treeList');
        $this->set(compact('categories'));
    }
    public function edit($id = null)
    {
        $article = $this->Articles->get($id, [
            'contain' => [],
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            if ($this->Articles->save($article)) {
                $this->Flash->success(__('The article has been saved.'));
 
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The article could not be saved. Please, try again.'));
        }
        $this->set(compact('article'));
        $categories = $this->Articles->Categories->find('treeList');
        $this->set(compact('categories'));
    }
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $article = $this->Articles->get($id);
        if ($this->Articles->delete($article)) {
            $this->Flash->success(__('The article has been deleted.'));
        } else {
            $this->Flash->error(__('The article could not be deleted. Please, try again.'));
        }
        return $this->redirect(['action' => 'index']);
    }
}
    // src/Model/Table/ArticlesTable.php
    // ...
    public function initialize(array $config)
    {
        // ...
        // Just add the belongsTo relation with CategoriesTable
        $this->belongsTo('Categories', [
            'foreignKey' => 'category_id',
        ]);
        // ...
    }
    // ...
<!-- File: src/Template/Articles/view.ctp -->
<h1><?=h($article->title)?></h1>
<p><?=h($article->body)?></p>
<p><small>Created: <?=$article->created->format(DATE_RFC850)?></small></p>
 
<!-- File: src/Template/Articles/add.ctp -->
<h1>Add Article</h1>
<?php
echo $this->Form->create($article); //<form method="post" action="/articles/add"> use the FormHelper to generate the opening tag for an HTML form
echo $this->Form->control('category_id');
echo $this->Form->control('title');
echo $this->Form->control('body', ['rows' => '3']);
echo $this->Form->button(__('Save Article'));
echo $this->Form->end();
?>
 
<!-- File: src/Template/Articles/index.ctp -->
<h1>Blog articles</h1>
<p><?=$this->Html->link('Add Article', ['action' => 'add'])?></p>
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Category</th>
<th>Created</th>
<th>Action</th>
</tr>
<!-- Here is where we iterate through our $articles query object, printing out article info -->
<?php foreach ($articles as $article): ?>
<tr>
<td><?=$article->id?></td>
<td>
<?=$this->Html->link($article->title, ['action' => 'view', $article->id])?>
<!-- You might have noticed the use of an object called $this->Html. This is an instance of the CakePHP
Cake\View\Helper\HtmlHelper class. -->
</td>
<td><?=$article->category_id?>
</td>
<td>
<?=$article->created->format(DATE_RFC850)?>
</td>
<td>
<!-- Using View\Helper\FormHelper::postLink() will create a link that uses JavaScript to do a POST request
deleting our article. -->
<?=$this->Form->postLink(
    'Delete',
    ['action' => 'delete', $article->id],
    ['confirm' => 'Are you sure?'])
?> |
<?=$this->Html->link('Edit', ['action' => 'edit', $article->id])?>
</td>
</tr>
<?php endforeach;?>
</table>

$ bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified
$ bin/cake migrations migrate
$ bin/cake bake model Categories
$ bin/cake bake controller Categories
$ bin/cake bake template Categories
<?php
// src/Controller/CategoriesController.php
namespace App\Controller;
use App\Controller\AppController;
class CategoriesController extends AppController
{
    public function index()
    {
 
        $this->paginate = [
            'limit' => 10,
            'order' => [
                'Categories.lft' => 'asc',
            ],
            'contain' => ['ParentCategories'],
        ];
        $categories = $this->paginate($this->Categories);
 
        $this->set(compact('categories'));
        $this->set('_serialize', ['categories']);
    }
    public function moveUp($id = null)
    {
        $this->request->allowMethod(['post', 'put']);
        $category = $this->Categories->get($id);
 
        if ($this->Categories->moveUp($category)) {
            $this->Flash->success('The category has been moved Up.');
        } else {
            $this->Flash->error('The category could not be moved up. Please, try again.');
        }
        return $this->redirect($this->referer(['action' => 'index']));
    }
    public function moveDown($id = null)
    {
        $this->request->allowMethod(['post', 'put']);
        $category = $this->Categories->get($id);
        if ($this->Categories->moveDown($category)) {
            $this->Flash->success('The category has been moved down.');
        } else {
            $this->Flash->error('The category could not be moved down. Please, try again.');
        }
        return $this->redirect($this->referer(['action' => 'index']));
    }
    public function view($id = null)
    {
        $category = $this->Categories->get($id, [
            'contain' => ['ParentCategories', 'Articles', 'ChildCategories'],
        ]);
 
        $this->set('category', $category);
    }
    public function add()
    {
        $category = $this->Categories->newEntity();
        if ($this->request->is('post')) {
            $category = $this->Categories->patchEntity($category, $this->request->getData());
            if ($this->Categories->save($category)) {
                $this->Flash->success(__('The category has been saved.'));
 
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The category could not be saved. Please, try again.'));
        }
        $parentCategories = $this->Categories->ParentCategories->find('list', ['limit' => 200]);
        $this->set(compact('category', 'parentCategories'));
    }
    public function edit($id = null)
    {
        $category = $this->Categories->get($id, [
            'contain' => [],
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $category = $this->Categories->patchEntity($category, $this->request->getData());
            if ($this->Categories->save($category)) {
                $this->Flash->success(__('The category has been saved.'));
 
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The category could not be saved. Please, try again.'));
        }
        $parentCategories = $this->Categories->ParentCategories->find('list', ['limit' => 200]);
        $this->set(compact('category', 'parentCategories'));
    }
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $category = $this->Categories->get($id);
        if ($this->Categories->delete($category)) {
            $this->Flash->success(__('The category has been deleted.'));
        } else {
            $this->Flash->error(__('The category could not be deleted. Please, try again.'));
        }
        return $this->redirect(['action' => 'index']);
    }
}
$ bin/cake bake migration CreateUsers username:string[50] password:string role:string[20] created modified
$ bin/cake migrations migrate
$ bin/cake bake model Users
// src/Model/Table/UsersTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UsersTable extends Table
{
    // ...
    public function validationDefault(Validator $validator)
    {
        // ...
        return $validator
            ->notEmpty('username', 'A username is required')
            ->notEmpty('password', 'A password is required')
            ->notEmpty('role', 'A role is required')
            ->add('role', 'inList', [
                'rule' => ['inList', ['admin', 'author']],
                'message' => 'Please enter a valid role',
            ]);
        // ...
    }
}
$ bin/cake bake controller Users
$ bin/cake bake template Users
<!-- src/Template/Users/add.ctp -->
// ...
    echo $this->Form->control('role', [
        'options' => ['admin' => 'Admin', 'author' => 'Author'],
    ]);
// ...
// src/Controller/AppController.php
use Cake\Event\Event; // Added this line
// ...
    public function initialize()
    {
        // ...
        $this->loadComponent('Auth', [
            'loginRedirect' => [
                'controller' => 'Articles',
                'action' => 'index'
            ],
            'logoutRedirect' => [
                'controller' => 'Pages',
                'action' => 'display',
                'home'
            ]
        ]);
    }
    /* What we did in the beforeFilter() function was to tell the AuthComponent to not require a login for all
index() and view() actions, in every controller. We want our visitors to be able to read and list the entries
without registering in the site. */
    public function beforeFilter(Event $event)
    {
        $this->Auth->allow(['index', 'view', 'display']);
    }
// src/Controller/UsersController.php
use Cake\Event\Event; // Added this line
// ...
    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);
        // Allow users to register and logout.
        // You should not add the "login" action to allow list. Doing so would
        // cause problems with normal functioning of AuthComponent.
        $this->Auth->allow(['add', 'logout']);
    }
 
    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(__('Invalid username or password, try again'));
        }
    }
    public function logout()
    {
        return $this->redirect($this->Auth->logout());
    }
// ...
// src/Model/Entity/User.php
use Cake\Auth\DefaultPasswordHasher; //include this line
// ...
class User extends Entity
{
// ...
    protected function _setPassword($password)
    {
        if (strlen($password) > 0) {
            return (new DefaultPasswordHasher)->hash($password);
        }
    }
// ...
}
<!-- File: src/Template/Users/login.ctp -->
<div class="users form">
<?= $this->Flash->render() ?>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Please enter your username and password') ?></legend>
<?= $this->Form->control('username') ?>
<?= $this->Form->control('password') ?>
</fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>
ALTER TABLE articles ADD COLUMN user_id INT(11);
// src/Controller/ArticlesController.php
// ...
    public function add()
    {
        // ...
        $article = $this->Articles->patchEntity($article, $this->request->getData());
        $article->user_id = $this->Auth->user('id'); // Added this line
        if ($this->Articles->save($article)) {
        // ...
    }
// ...
// src/Controller/AppController.php
// ...
    public function initialize()
    {
        // ...
        $this->loadComponent('Auth', [
            'authorize' => ['Controller'], // Added this line
            'loginRedirect' => [
                'controller' => 'Articles',
                'action' => 'index',
            ],
            'logoutRedirect' => [
                'controller' => 'Pages',
                'action' => 'display',
                'home',
            ],
        ]);
    }
 
    public function isAuthorized($user)
    {
        // Admin can access every action
        if (isset($user['role']) && $user['role'] === 'admin') {
            return true;
        }
        // Default deny
        return false;
    }
// src/Controller/ArticlesController.php
// ...
    public function isAuthorized($user)
    {
        // All registered users can add articles
        // Prior to 3.4.0 $this->request->param('action') was used.
        if ($this->request->getParam('action') === 'add') {
            return true;
        }
        // The owner of an article can edit and delete it
        // Prior to 3.4.0 $this->request->param('action') was used.
        if (in_array($this->request->getParam('action'), ['edit', 'delete'])) {
            // Prior to 3.4.0 $this->request->params('pass.0')
            $articleId = (int) $this->request->getParam('pass.0');
            if ($this->Articles->isOwnedBy($articleId, $user['id'])) {
                return true;
            }
        }
        return parent::isAuthorized($user);
    }
// ...
// src/Model/Table/ArticlesTable.php
// ...
    public function isOwnedBy($articleId, $userId)
    {
        return $this->exists(['id' => $articleId, 'user_id' => $userId]);
    }
// ...

One thought on “CakePHP3ブログチュートリアル

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.