[CakePHP4] Content Management Tutorial

– Make sure to get PHP version.
$ php -v
PHP 7.4.3 (cli) (built: Mar 2 2022 15:36:52) ( NTS )

$ php composer.phar create-project –prefer-dist cakephp/app:4.* cms
$ bin/cake server -H 0.0.0.0

# create database cake_cms;
# \c cake_cms;

CREATE TABLE users(
	id SERIAL PRIMARY KEY,
	email VARCHAR(255) NOT NULL,
	password VARCHAR(255) NOT NULL,
	created TIMESTAMP,
	modified TIMESTAMP
);

CREATE TABLE articles(
	id SERIAL PRIMARY KEY,
	user_id INT NOT NULL,
	title VARCHAR(255) NOT NULL,
	slug VARCHAR(191) NOT NULL,
	body TEXT,
	published BOOLEAN DEFAULT FALSE,
	created TIMESTAMP,
	modified TIMESTAMP,
	UNIQUE (slug),
	FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE tags(
	id SERIAL PRIMARY KEY,
	title VARCHAR(191),
	created TIMESTAMP,
	modified TIMESTAMP,
	UNIQUE (title)
);

CREATE TABLE articles_tags(
	article_id INT NOT NULL,
	tag_id INT NOT NULL,
	PRIMARY KEY (article_id, tag_id),
	FOREIGN KEY (tag_id) REFERENCES tags(id),
	FOREIGN KEY (article_id) REFERENCES articles(id)
);

INSERT INTO users (email, password, created, modified) VALUES 
('cakephp@example.com', 'secret', NOW(), NOW());

INSERT INTO articles(user_id, title, slug, body, published, created, modified)
VALUES
(1, 'first post', 'first-post', 'This is the first post.', TRUE, NOW(), NOW());

cake_cms=# select * from users;
cake_cms=# select * from articles;

Database Configuration
L app_local.php

'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Postgres',
            'persistent' => false,
            'host' => 'localhost',
            //'port' => 'non_standard_port_number',
            'username' => 'hoge',
            'password' => 'hoge',
            'database' => 'cake_cms',
            // 'encoding' => 'utf8mb4',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
            'url' => env('DATABASE_URL', null),
        ],

Creating our first model
src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table {

	public function initialize(array $config): void {
		$this->addBehavior('Timestamp');
	}
}

src/Model/Entity/Article.php

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Article extends Entity {

	protected $_accessible = [
		'*' => true,
		'id' => false,
		'slug' => false
	];
}

Creating the Articles Controller
src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController {

	public function index() {
		$this->loadComponent('Paginator');
		$articles = $this->Paginator->paginate($this->Articles->find());
		$this->set(compact('articles'));
	}
}

Create the Article List Template
templates/Articles/index.php

<h1>Articles</h1>
<table>
	<tr>
		<th>Title</th>
		<th>Created</th>
	</tr>

	<?php foreach ($articles as $article): ?>
	<tr>
		<td>
			<?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
		</td>
		<td>
			<?= $article->created->format(DATE_RFC850) ?>
		</td>
	</tr>
	<?php endforeach; ?>
</table>

Create the View Action
src/Controller/ArticlesController.php

	public function view($slug = null){

		$article = $this->Articles->findBySlug($slug)->firstOrFail();
		$this->set(compact('article'));
	}

Create the view Template
templates/Articles/view.php

<h1><?= h($article->title) ?></h1>
<p><?= h($article->body) ?></p>
<p><small>Created: <?= $article->created->format(DATE_RFC850) ?></small></p>
<p><?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?></p>

Adding Articles

class ArticlesController extends AppController {

	public function initialize(): void {
		parent::initialize();

		$this->loadComponent('Paginator');
		$this->loadComponent('Flash');
	}

	public function index() {
		$articles = $this->Paginator->paginate($this->Articles->find());
		$this->set(compact('articles'));
	}

	public function view($slug = null){

		$article = $this->Articles->findBySlug($slug)->firstOrFail();
		$this->set(compact('article'));
	}

	public function add(){

		$article = $this->Articles->newEmptyEntity();
		if($this->request->is('post')){
			$article = $this->Articles->patchEntity($article, $this->request->getData());

			$article->user_id = 1;

			if ($this->Articles->save($article)){
				$this->Flash->success(__('Your article has been saved.'));
				return $this->redirect(['action' => 'index']);
			}
			$this->Flash->error(__('Unable to add your article.'));
		}
		$this->set('article', $article);
	}
}

Create Add Template
templates/Articles/add.php

<h1>Add Article</h1>
<?php
	echo $this->Form->create($article);
	echo $this->Form->control('user_id', ['type'=> 'hidden', 'value' => 1]);
	echo $this->Form->control('title');
	echo $this->Form->control('body', ['rows' => '3']);
	echo $this->Form->button(__('Save Article'));
	echo $this->Form->end();
?>

templates/Articles/index.php

<?= $this->Html->link('Add Article', ['action' => 'add']) ?>

Adding Simple Slug Generation
src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Utility\Text;
use Cake\Event\EventInterface;

public function beforeSave(EventInterface $event, $entity, $options){
	if($entity->isNew() && !$entity->slug){
		$sluggedTitle = Text::slug($entity->title);
		$entity->slug = substr($sluggedTitle, 0, 191);
	}
}

Add Edit Action

	public function edit($slug) {
		$article = $this->Articles
			->findBySlug($slug)
			->firstOrFail();

		if($this->request->is(['post', 'put'])){
			$this->Articles->patchEntity($article, $this->request->getData());
			if ($this->Articles->save($article)){
				$this->Flash->success(__('Your article has been updated.'));
				return $this->redirect(['action' => 'index']);
			}
			$this->Flash->error(__('Unable to update your article.'));
		}
		$this->set('article', $article);
	}

Create Edit Template

<h1>Edit Article</h1>
<?php
	echo $this->Form->create($article);
	echo $this->Form->control('user_id', ['type'=> 'hidden']);
	echo $this->Form->control('title');
	echo $this->Form->control('body', ['rows' => '3']);
	echo $this->Form->button(__('Save Article'));
	echo $this->Form->end();
?>

templates/Articles/index.php

<h1>Articles</h1>
<?= $this->Html->link('Add Article', ['action' => 'add']) ?>
<table>
	<tr>
		<th>Title</th>
		<th>Created</th>
		<th>Action</th>
	</tr>

	<?php foreach ($articles as $article): ?>
	<tr>
		<td>
			<?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
		</td>
		<td>
			<?= $article->created->format(DATE_RFC850) ?>
		</td>
		<td>
			<?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?>
		</td>
	</tr>
	<?php endforeach; ?>
</table>