From fcde462e7e1cd63fad6608c9fd9d90361e89dd8b Mon Sep 17 00:00:00 2001 From: Joe Theuerkauf Date: Mon, 25 Dec 2023 10:17:30 -0500 Subject: [PATCH] Remove "blog" tutorial (#7786) * Completely remove docs for the blog tutorial - caused odd page continuity * Add tips for using bake to create structures and migrations * Split database.rst so the Articles model is a separate page * Minor grammar and phrasing updates --- en/migrations.rst | 2 +- en/tutorials-and-examples.rst | 11 +- .../blog-auth-example/auth.rst | 381 ---------- en/tutorials-and-examples/blog/blog.rst | 233 ------ en/tutorials-and-examples/blog/part-three.rst | 391 ---------- en/tutorials-and-examples/blog/part-two.rst | 696 ------------------ .../cms/articles-controller.rst | 12 +- .../cms/articles-model.rst | 85 +++ en/tutorials-and-examples/cms/database.rst | 112 ++- .../cms/installation.rst | 20 +- 10 files changed, 155 insertions(+), 1788 deletions(-) delete mode 100644 en/tutorials-and-examples/blog-auth-example/auth.rst delete mode 100755 en/tutorials-and-examples/blog/blog.rst delete mode 100644 en/tutorials-and-examples/blog/part-three.rst delete mode 100644 en/tutorials-and-examples/blog/part-two.rst create mode 100644 en/tutorials-and-examples/cms/articles-model.rst diff --git a/en/migrations.rst b/en/migrations.rst index 51dcf6e52e..00218e316e 100644 --- a/en/migrations.rst +++ b/en/migrations.rst @@ -1,4 +1,4 @@ Migrations ########## -This page has `moved `__. +This page has `moved `__. diff --git a/en/tutorials-and-examples.rst b/en/tutorials-and-examples.rst index 07c26d5134..788172a197 100644 --- a/en/tutorials-and-examples.rst +++ b/en/tutorials-and-examples.rst @@ -14,19 +14,12 @@ and components. tutorials-and-examples/cms/installation tutorials-and-examples/cms/database + tutorials-and-examples/cms/articles-model tutorials-and-examples/cms/articles-controller tutorials-and-examples/cms/tags-and-users tutorials-and-examples/cms/authentication tutorials-and-examples/cms/authorization -.. toctree:: - :hidden: - - tutorials-and-examples/blog/blog - tutorials-and-examples/blog/part-two - tutorials-and-examples/blog/part-three - tutorials-and-examples/blog-auth-example/auth - .. meta:: :title lang=en: Tutorials & Examples - :keywords lang=en: application tutorials,glob,bakery,repository,applications,blog,acl + :keywords lang=en: application tutorials,glob,bakery,repository,applications,blog,cms,acl diff --git a/en/tutorials-and-examples/blog-auth-example/auth.rst b/en/tutorials-and-examples/blog-auth-example/auth.rst deleted file mode 100644 index c4f9277fe1..0000000000 --- a/en/tutorials-and-examples/blog-auth-example/auth.rst +++ /dev/null @@ -1,381 +0,0 @@ -Blog Tutorial - Authentication -############################## - -Following our :doc:`/tutorials-and-examples/blog/blog` example, imagine we -wanted to disallow unauthenticated users to create articles. - -Creating Users Table and Controller -=================================== - -First, let's create a new table in our blog database to hold our users' data: - -.. code-block:: mysql - - CREATE TABLE users ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - email VARCHAR(255), - password VARCHAR(255), - role VARCHAR(20), - created DATETIME DEFAULT NULL, - modified DATETIME DEFAULT NULL - ); - -If you are using PostgreSQL, connect to cake_blog database and execute the following SQL instead: - -.. code-block:: SQL - - CREATE TABLE users ( - id SERIAL PRIMARY KEY, - email VARCHAR(255), - password VARCHAR(255), - role VARCHAR(20), - created TIMESTAMP DEFAULT NULL, - modified TIMESTAMP DEFAULT NULL - ); - -We have adhered to the CakePHP conventions in naming tables, but we're also -taking advantage of another convention: By using the email and password -columns in a users table, CakePHP will be able to auto-configure most things for -us when implementing the user login. - -Next step is to create our ``UsersTable`` class, responsible for finding, saving -and validating any user data:: - - // 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): Validator - { - return $validator - ->notEmpty('email', 'An email is required') - ->email('email') - ->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' - ]); - } - - } - -Let's also create our ``UsersController``. The following content corresponds to -parts of a basic baked ``UsersController`` class using the code generation -utilities bundled with CakePHP:: - - // src/Controller/UsersController.php - - namespace App\Controller; - - use App\Controller\AppController; - use Cake\Event\EventInterface; - - class UsersController extends AppController - { - public function index() - { - $this->set('users', $this->Users->find()->all()); - } - - public function view($id) - { - $user = $this->Users->get($id); - $this->set(compact('user')); - } - - public function add() - { - $user = $this->Users->newEmptyEntity(); - if ($this->request->is('post')) { - $user = $this->Users->patchEntity($user, $this->request->getData()); - if ($this->Users->save($user)) { - $this->Flash->success(__('The user has been saved.')); - return $this->redirect(['action' => 'add']); - } - $this->Flash->error(__('Unable to add the user.')); - } - $this->set('user', $user); - } - } - -In the same way we created the views for our articles by using the code -generation tool, we can implement the user views. For the purpose of this -tutorial, we will show just the **add.php**: - -.. code-block:: php - - - -
- Form->create($user) ?> -
- - Form->control('email') ?> - Form->control('password') ?> - Form->control('role', [ - 'options' => ['admin' => 'Admin', 'author' => 'Author'] - ]) ?> -
- Form->button(__('Submit')); ?> - Form->end() ?> -
- -Adding Authentication -===================== - -We're now ready to add our authentication layer. In CakePHP this is handled by -the ``authentication`` plugin. Let's start off by installing it. Use composer to -install the Authentication Plugin: - -.. code-block:: console - - php composer.phar require "cakephp/authentication:^2.0" - -Then add the following to your application's ``bootstrap()`` method:: - - // in src/Application.php in the bootstrap() method. - $this->addPlugin('Authentication'); - -Adding Password Hashing -======================= - -Next, we'll create the ``User`` entity and add password hashing. Create the -**src/Model/Entity/User.php** entity file and add the following:: - - // src/Model/Entity/User.php - namespace App\Model\Entity; - - use Authentication\PasswordHasher\DefaultPasswordHasher; - use Cake\ORM\Entity; - - class User extends Entity - { - // Make all fields mass assignable except for primary key field "id". - protected array $_accessible = [ - '*' => true, - 'id' => false - ]; - - // ... - - protected function _setPassword($password) - { - if (strlen($password) > 0) { - return (new DefaultPasswordHasher)->hash($password); - } - } - - // ... - } - -Now every time the password property is assigned to the user it will be hashed -using the ``DefaultPasswordHasher`` class. - -Configuring Authentication -========================== - -Now it's time to configure the Authentication Plugin. -The Plugin will handle the authentication process using 3 different classes: - -* ``Application`` will use the Authentication Middleware and provide an - AuthenticationService, holding all the configuration we want to define how are - we going to check the credentials, and where to find them. -* ``AuthenticationService`` will be a utility class to allow you configure the - authentication process. -* ``AuthenticationMiddleware`` will be executed as part of the middleware queue, - this is before your Controllers are processed by the framework, and will pick the - credentials and process them to check if the user is authenticated. - -Authentication logic is divided into specific classes and the authentication -process happens before your controller layer. First authentication checks if the -user is authenticated (based in the configuration you provided) and injects the -user and the authentication results into the request for further reference. - -In **src/Application.php**, add the following imports:: - - // In src/Application.php add the following imports - use Authentication\AuthenticationService; - use Authentication\AuthenticationServiceInterface; - use Authentication\AuthenticationServiceProviderInterface; - use Authentication\Middleware\AuthenticationMiddleware; - use Psr\Http\Message\ServerRequestInterface; - -Then implement the authentication interface on your application class:: - - // in src/Application.php - class Application extends BaseApplication - implements AuthenticationServiceProviderInterface - { - -Then add the following:: - - // src/Application.php - public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue - { - $middlewareQueue - // ... other middleware added before - ->add(new RoutingMiddleware($this)) - // add Authentication after RoutingMiddleware - ->add(new AuthenticationMiddleware($this)); - - return $middlewareQueue; - } - - public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface - { - $authenticationService = new AuthenticationService([ - 'unauthenticatedRedirect' => '/users/login', - 'queryParam' => 'redirect', - ]); - - // Load identifiers, ensure we check email and password fields - $authenticationService->loadIdentifier('Authentication.Password', [ - 'fields' => [ - 'username' => 'email', - 'password' => 'password', - ], - ]); - - // Load the authenticators, you want session first - $authenticationService->loadAuthenticator('Authentication.Session'); - // Configure form data check to pick email and password - $authenticationService->loadAuthenticator('Authentication.Form', [ - 'fields' => [ - 'username' => 'email', - 'password' => 'password', - ], - 'loginUrl' => '/users/login', - ]); - - return $authenticationService; - } - -In you ``AppController`` class add the following code:: - - // src/Controller/AppController.php - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Flash'); - - // Add this line to check authentication result and lock your site - $this->loadComponent('Authentication.Authentication'); - -Now, on every request, the ``AuthenticationMiddleware`` will inspect the request -session to look for an authenticated user. If we are loading the -``/users/login`` page, it'll inspect also the posted form data (if any) to -extract the credentials. By default the credentials will be extracted from the -``email`` and ``password`` fields in the request data. The authentication -result will be injected in a request attribute named ``authentication``. You can -inspect the result at any time using -``$this->request->getAttribute('authentication')`` from your controller actions. -All your pages will be restricted as the ``AuthenticationComponent`` is checking -the result on every request. When it fails to find any authenticated user, it'll -redirect the user to the ``/users/login`` page. Note at this point, the site -won't work as we don't have a login page yet. If you visit your site, you'll -get an "infinite redirect loop". So, let's fix that! - -In your ``UsersController``, add the following code:: - - public function beforeFilter(\Cake\Event\EventInterface $event) - { - parent::beforeFilter($event); - // Configure the login action to not require authentication, preventing - // the infinite redirect loop issue - $this->Authentication->addUnauthenticatedActions(['login']); - } - - public function login() - { - $this->request->allowMethod(['get', 'post']); - $result = $this->Authentication->getResult(); - // regardless of POST or GET, redirect if user is logged in - if ($result->isValid()) { - // redirect to /articles after login success - $redirect = $this->request->getQuery('redirect', [ - 'controller' => 'Articles', - 'action' => 'index', - ]); - - return $this->redirect($redirect); - } - // display error if user submitted and authentication failed - if ($this->request->is('post') && !$result->isValid()) { - $this->Flash->error(__('Invalid email or password')); - } - } - -Add the template logic for your login action:: - - -
- Flash->render() ?> -

Login

- Form->create() ?> -
- - Form->control('email', ['required' => true]) ?> - Form->control('password', ['required' => true]) ?> -
- Form->submit(__('Login')); ?> - Form->end() ?> - - Html->link("Add User", ['action' => 'add']) ?> -
- -Now login page will allow us to correctly login into the application. -Test it by requesting any page of your site. After being redirected -to the ``/users/login`` page, enter the email and password you -picked previously when creating your user. You should be redirected -successfully after login. - -We need to add a couple more details to configure our application. We want all -``view`` and ``index`` pages accessible without logging in so we'll add this -specific configuration in ``AppController``:: - - // in src/Controller/AppController.php - public function beforeFilter(\Cake\Event\EventInterface $event) - { - parent::beforeFilter($event); - // for all controllers in our application, make index and view - // actions public, skipping the authentication check. - $this->Authentication->addUnauthenticatedActions(['index', 'view']); - } - -Logout -====== - -Add the logout action to the ``UsersController`` class:: - - // in src/Controller/UsersController.php - public function logout() - { - $result = $this->Authentication->getResult(); - // regardless of POST or GET, redirect if user is logged in - if ($result->isValid()) { - $this->Authentication->logout(); - return $this->redirect(['controller' => 'Users', 'action' => 'login']); - } - } - -Now you can visit ``/users/logout`` to log out. You should then be sent to the -login page. If you've made it this far, congratulations, you now have a simple -blog that: - -* Allows authenticated users to create and edit articles. -* Allows unauthenticated users to view articles and tags. - -Suggested Follow-up Reading ---------------------------- - -#. :doc:`/bake/usage` Generating basic CRUD code -#. `Authentication Plugin `__ documentation. - -.. meta:: - :title lang=en: Simple Authentication Application - :keywords lang=en: auto increment,authorization application,model user,array,conventions,authentication,urls,cakephp,delete,doc,columns diff --git a/en/tutorials-and-examples/blog/blog.rst b/en/tutorials-and-examples/blog/blog.rst deleted file mode 100755 index 498a9a3b03..0000000000 --- a/en/tutorials-and-examples/blog/blog.rst +++ /dev/null @@ -1,233 +0,0 @@ -Blog Tutorial -############# - -This tutorial will walk you through the creation of a simple blog application. -We'll be installing CakePHP, creating a database, and creating enough -application logic to list, add, edit, and delete blog articles. - -Here's what you'll need: - -#. A running web server. We're going to assume you're using Apache, - though the instructions for using other servers should be very - similar. We might have to play a little with the server - configuration, but most folks can get CakePHP up and running without - any configuration at all. Make sure you have PHP |minphpversion| or greater, and - that the ``mbstring`` and ``intl`` extensions are enabled in PHP. -#. A database server. We're going to be using MySQL server in this - tutorial. You'll need to know enough about SQL in order to create a - database: CakePHP will be taking the reins from there. Since we're using MySQL, - also make sure that you have ``pdo_mysql`` enabled in PHP. -#. Basic PHP knowledge. - -Let's get started! - -Getting CakePHP -=============== - -The easiest way to install CakePHP is to use Composer. Composer is a simple way -of installing CakePHP from your terminal or command line prompt. First, you'll -need to download and install Composer if you haven't done so already. If you -have cURL installed, it's as easy as running the following:: - - curl -s https://getcomposer.org/installer | php - -Or, you can download ``composer.phar`` from the -`Composer website `_. - -Then simply type the following line in your terminal from your -installation directory to install the CakePHP application skeleton -in the directory that you wish to use it with. For this example we will be using -"blog" but feel free to change it to something else.:: - - php composer.phar create-project --prefer-dist cakephp/app:4.* blog - -In case you've already got composer installed globally, you may instead type:: - - composer self-update && composer create-project --prefer-dist cakephp/app:4.* blog - -The advantage to using Composer is that it will automatically complete some -important set up tasks, such as setting the correct file permissions and -creating your config/app.php file for you. - -There are other ways to install CakePHP. If you cannot or don't want to use -Composer, check out the :doc:`/installation` section. - -Regardless of how you downloaded and installed CakePHP, once your set up is -completed, your directory setup should look something like the following:: - - cake_install/ - bin/ - config/ - logs/ - plugins/ - src/ - tests/ - tmp/ - vendor/ - webroot/ - .editorconfig - .gitignore - .htaccess - .travis.yml - composer.json - index.php - phpunit.xml.dist - README.md - -Now might be a good time to learn a bit about how CakePHP's directory -structure works: check out the -:doc:`/intro/cakephp-folder-structure` section. - -Directory Permissions on tmp and logs -===================================== - -The ``tmp`` and ``logs`` directories need to have proper permissions to be writable -by your webserver. If you used Composer for the install, this should have been done -for you and confirmed with a "Permissions set on " message. If you instead -got an error message or want to do it manually, the best way would be to find out -what user your webserver runs as (````) and change the ownership of -these two directories to that user. The final command you run (in \*nix) -might look something like this:: - - chown -R www-data tmp - chown -R www-data logs - -If for some reason CakePHP can't write to these directories, you'll be -informed by a warning while not in production mode. - -While not recommended, if you are unable to set the permissions to the same as -your webserver, you can simply set write permissions on the folder by running a -command such as:: - - chmod -R 777 tmp - chmod -R 777 logs - -Creating the Blog Database -========================== - -Next, let's set up the underlying MySQL database for our blog. If you -haven't already done so, create an empty database for use in this -tutorial with the name of your choice such as ``cake_blog``. Right now, -we'll just create a single table to store our articles. - -.. code-block:: mysql - - # First, create our articles table - CREATE TABLE articles ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(50), - body TEXT, - created DATETIME DEFAULT NULL, - modified DATETIME DEFAULT NULL - ); - -If you are using PostgreSQL, connect to cake_blog database and execute the following SQL instead: - -.. code-block:: SQL - - -- First, create our articles table - CREATE TABLE articles ( - id SERIAL PRIMARY KEY, - title VARCHAR(50), - body TEXT, - created TIMESTAMP DEFAULT NULL, - modified TIMESTAMP DEFAULT NULL - ); - -We'll also throw in a few articles to use for testing purposes. Execute the following -SQL statements into your database (works for both MySQL and PostgreSQL): - -.. code-block:: mysql - - # Then insert some articles for testing: - INSERT INTO articles (title,body,created) - VALUES ('The title', 'This is the article body.', NOW()); - INSERT INTO articles (title,body,created) - VALUES ('A title once again', 'And the article body follows.', NOW()); - INSERT INTO articles (title,body,created) - VALUES ('Title strikes back', 'This is really exciting! Not.', NOW()); - -The choices on table and column names are not arbitrary. If you -follow CakePHP's database naming conventions, and CakePHP's class naming -conventions (both outlined in -:doc:`/intro/conventions`), you'll be able to take -advantage of a lot of free functionality and avoid configuration. -CakePHP is flexible enough to accommodate even inconsistent legacy -database schemas, but adhering to the conventions will save you time. - -Check out :doc:`/intro/conventions` for more -information, but it's suffice to say that naming our table 'articles' -automatically hooks it to our Articles model, and having fields called -'modified' and 'created' will be automatically managed by CakePHP. - -Database Configuration -====================== - -Next, let's tell CakePHP where our database is and how to connect to it. -For many, this will be the first and last time you will need to configure -anything. - -The configuration should be pretty straightforward: just replace the -values in the ``Datasources.default`` array in the **config/app.php** file -with those that apply to your setup. A sample completed configuration -array might look something like the following:: - - return [ - // More configuration above. - 'Datasources' => [ - 'default' => [ - 'className' => 'Cake\Database\Connection', - 'driver' => 'Cake\Database\Driver\Mysql', - 'persistent' => false, - 'host' => 'localhost', - 'username' => 'cake_blog', - 'password' => 'AngelF00dC4k3~', - 'database' => 'cake_blog', - 'encoding' => 'utf8', - 'timezone' => 'UTC', - ], - ], - // More configuration below. - ]; - -Once you've saved your **config/app.php** file, you should be able to open -your browser and see the CakePHP welcome page. It should also tell -you that your database connection file was found, and that CakePHP -can successfully connect to the database. - -.. note:: - - A copy of CakePHP's default configuration file is found in - **config/app.default.php**. - -Optional Configuration -====================== - -There are a few other items that can be configured. Most developers -complete these laundry-list items, but they're not required for -this tutorial. One is defining a custom string (or "salt") for use -in security hashes. - -The security salt is used for generating hashes. If you used Composer this too is taken -care of for you during the install. Else you'd need to change the default salt value -by editing **config/app.php**. It doesn't matter much what the new value is, as long as -it's not guessable:: - - 'Security' => [ - 'salt' => 'something long and containing lots of different values.', - ], - -A Note on mod\_rewrite -====================== - -Occasionally new users will run into mod\_rewrite issues. For example -if the CakePHP welcome page looks a little funny (no images or CSS styles). -This probably means mod\_rewrite is not functioning on your system. Please refer -to the :ref:`url-rewriting` section to help resolve any issues you are having. - -Now continue to :doc:`/tutorials-and-examples/blog/part-two` to start building -your first CakePHP application. - -.. meta:: - :title lang=en: Blog Tutorial - :keywords lang=en: model view controller,object oriented programming,application logic,directory setup,basic knowledge,database server,server configuration,reins,documentroot,readme,repository,web server,productivity,lib,sql,aim,cakephp,servers,apache,downloads diff --git a/en/tutorials-and-examples/blog/part-three.rst b/en/tutorials-and-examples/blog/part-three.rst deleted file mode 100644 index b40a02c8ad..0000000000 --- a/en/tutorials-and-examples/blog/part-three.rst +++ /dev/null @@ -1,391 +0,0 @@ -Blog Tutorial - Part 3 -###################### - -Create a Tree Category -====================== - -Let's continue our blog application and imagine we want to categorize our -articles. We want the categories to be ordered, and for this, we will use the -:doc:`Tree behavior ` to help us organize the -categories. - -But first, we need to modify our tables. - -Migrations Plugin -================= - -We will use the `migrations plugin `_ to -create a table in our database. If you already have an articles table in your -database, erase it. - -Now open your application's **composer.json** file. Normally you would see that -the migrations plugin is already under ``require``. If not, add it by executing:: - - composer require cakephp/migrations:~1.0 - -The migrations plugin will now be in your application's **plugins** folder. -Also, add ``$this->addPlugin('Migrations');`` to your application's ``bootstrap`` method. - -Once the plugin is loaded, run the following command to create a migration file:: - - bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified - -A migration file will be generated in the **/config/Migrations** folder with the following:: - - table('articles'); - $table->addColumn('title', 'string', [ - 'default' => null, - 'limit' => 255, - 'null' => false, - ]); - $table->addColumn('body', 'text', [ - 'default' => null, - 'null' => false, - ]); - $table->addColumn('category_id', 'integer', [ - 'default' => null, - 'limit' => 11, - 'null' => false, - ]); - $table->addColumn('created', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->addColumn('modified', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->create(); - } - } - -Run another command to create a ``categories`` table. If you need to specify -a field length, you can do it within brackets in the field type, ie:: - - bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified - -This will generate the following file in **config/Migrations**:: - - table('categories'); - $table->addColumn('parent_id', 'integer', [ - 'default' => null, - 'limit' => 11, - 'null' => false, - ]); - $table->addColumn('lft', 'integer', [ - 'default' => null, - 'limit' => 10, - 'null' => false, - ]); - $table->addColumn('rght', 'integer', [ - 'default' => null, - 'limit' => 10, - 'null' => false, - ]); - $table->addColumn('name', 'string', [ - 'default' => null, - 'limit' => 100, - 'null' => false, - ]); - $table->addColumn('description', 'string', [ - 'default' => null, - 'limit' => 255, - 'null' => false, - ]); - $table->addColumn('created', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->addColumn('modified', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->create(); - } - } - -Now that the migration files are created, you can edit them before creating -your tables. We need to change the ``'null' => false`` for the ``parent_id`` -field with ``'null' => true`` because a top-level category has a null -``parent_id``. - -Run the following command to create your tables:: - - bin/cake migrations migrate - -Modifying the Tables -==================== - -With our tables set up, we can now focus on categorizing our articles. - -We suppose you already have the files (Tables, Controllers and Templates of -Articles) from part 2. So we'll just add the references to categories. - -We need to associate the Articles and Categories tables together. Open -the **src/Model/Table/ArticlesTable.php** file and add the following:: - - // 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'); - // Just add the belongsTo relation with CategoriesTable - $this->belongsTo('Categories', [ - 'foreignKey' => 'category_id', - ]); - } - } - -Generate Skeleton Code for Categories -===================================== - -Create all files by launching bake commands:: - - bin/cake bake model Categories - bin/cake bake controller Categories - bin/cake bake template Categories - -Alternatively, you can bake all with just one line:: - - bin/cake bake all Categories - -The bake tool has created all your files in a snap. You can give them a quick -read if you want re-familiarize yourself with how CakePHP works. - -.. note:: - If you are on Windows remember to use \\ instead of /. - -You'll need to edit the following in **templates/Categories/add.php** -and **templates/Categories/edit.php**:: - - echo $this->Form->control('parent_id', [ - 'options' => $parentCategories, - 'empty' => 'No parent category' - ]); - -Attach TreeBehavior to CategoriesTable -====================================== - -The :doc:`TreeBehavior ` helps you manage hierarchical Tree -structures in database table. It uses the `MPTT logic -`_ to manage the data. -MPTT tree structures are optimized for reads, which often makes them a good fit -for read heavy applications like blogs. - -If you open the **src/Model/Table/CategoriesTable.php** file, you'll see -that the TreeBehavior has been attached to your CategoriesTable in the -``initialize()`` method. Bake adds this behavior to any Tables that contain -``lft`` and ``rght`` columns:: - - $this->addBehavior('Tree'); - -With the TreeBehavior attached you'll be able to access some features like -reordering the categories. We'll see that in a moment. - -But for now, you have to remove the following controls in your Categories add and -edit template files:: - - echo $this->Form->control('lft'); - echo $this->Form->control('rght'); - -In addition you should disable or remove the requirePresence from the validator -for both the ``lft`` and ``rght`` columns in your CategoriesTable model:: - - public function validationDefault(Validator $validator): Validator - { - $validator - ->add('id', 'valid', ['rule' => 'numeric']) - ->allowEmptyString('id', 'create'); - - $validator - ->add('lft', 'valid', ['rule' => 'numeric']) - // ->requirePresence('lft', 'create') - ->notEmpty('lft'); - - $validator - ->add('rght', 'valid', ['rule' => 'numeric']) - // ->requirePresence('rght', 'create') - ->notEmpty('rght'); - } - -These fields are automatically managed by the TreeBehavior when -a category is saved. - -Using your web browser, add some new categories using the -``/yoursite/categories/add`` controller action. - -Reordering Categories with TreeBehavior -======================================= - -In your categories index template file, you can list the categories and re-order -them. - -Let's modify the index method in your **CategoriesController.php** and add -``moveUp()`` and ``moveDown()`` methods to be able to reorder the categories in -the tree:: - - class CategoriesController extends AppController - { - public function index() - { - $categories = $this->Categories->find() - ->order(['lft' => 'ASC']) - ->all(); - $this->set(compact('categories')); - $this->viewBuilder()->setOption('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'])); - } - } - -In **templates/Categories/index.php** replace the existing content with:: - -
-

-
    -
  • Html->link(__('New Category'), ['action' => 'add']) ?>
  • -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
IdParent IdLftRghtNameDescriptionCreated
id ?>parent_id ?>lft ?>rght ?>name) ?>description) ?>created) ?> - Html->link(__('View'), ['action' => 'view', $category->id]) ?> - Html->link(__('Edit'), ['action' => 'edit', $category->id]) ?> - Form->postLink(__('Delete'), ['action' => 'delete', $category->id], ['confirm' => __('Are you sure you want to delete # {0}?', $category->id)]) ?> - Form->postLink(__('Move down'), ['action' => 'moveDown', $category->id], ['confirm' => __('Are you sure you want to move down # {0}?', $category->id)]) ?> - Form->postLink(__('Move up'), ['action' => 'moveUp', $category->id], ['confirm' => __('Are you sure you want to move up # {0}?', $category->id)]) ?> -
-
- -Modifying the ArticlesController -================================ - -In our ``ArticlesController``, we'll get the list of all the categories. -This will allow us to choose a category for an Article when creating or editing -it:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - use Cake\Http\Exception\NotFoundException; - - class ArticlesController extends AppController - { - // ... - - public function add() - { - $article = $this->Articles->newEmptyEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - 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); - - // Just added the categories list to be able to choose - // one category for an article - $categories = $this->Articles->Categories->find('treeList')->all(); - $this->set(compact('categories')); - } - } - -Modifying the Articles Templates -================================ - -The article add file should look something like this: - -.. code-block:: php - - - -

Add Article

- Form->create($article); - // just added the categories control - 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(); - -When you go to the address ``/yoursite/articles/add`` you should see a list -of categories to choose. - -.. meta:: - :title lang=en: Blog Tutorial Migrations and Tree - :keywords lang=en: doc models,migrations,tree,controller actions,model article,php class,model class,model object,business logic,database table,naming convention,bread and butter,callbacks,prefixes,nutshell,interaction,array,cakephp,interface,applications,delete diff --git a/en/tutorials-and-examples/blog/part-two.rst b/en/tutorials-and-examples/blog/part-two.rst deleted file mode 100644 index 150846a883..0000000000 --- a/en/tutorials-and-examples/blog/part-two.rst +++ /dev/null @@ -1,696 +0,0 @@ -Blog Tutorial - Part 2 -###################### - -Create an Article Model -======================= - -Models are the bread and butter of CakePHP applications. By -creating a CakePHP model that will interact with our database, -we'll have the foundation in place needed to do our view, add, -edit, and delete operations later. - -CakePHP's model class files are split between ``Table`` and ``Entity`` objects. -``Table`` objects provide access to the collection of entities stored in a -specific table and go in **src/Model/Table**. The file we'll be creating will -be saved to **src/Model/Table/ArticlesTable.php**. The completed file should -look like this:: - - // 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'); - } - } - -Naming conventions are very important in CakePHP. By naming our Table object -``ArticlesTable``, CakePHP can automatically infer that this Table object will -be used in the ``ArticlesController``, and will be tied to a database table called -``articles``. - -.. note:: - - CakePHP will dynamically create a model object for you if it - cannot find a corresponding file in **src/Model/Table**. This also means - that if you accidentally name your file wrong (i.e. articlestable.php or - ArticleTable.php), CakePHP will not recognize any of your settings and will - use the generated model instead. - -For more on models, such as callbacks, and validation, check out the :doc:`/orm` -chapter of the Manual. - -.. note:: - - If you completed :doc:`Part 1 of the Blog Tutorial - ` and created the ``articles`` table in - our Blog database you can leverage CakePHP's bake console and its code - generation capabilities to create the ``ArticlesTable`` model:: - - bin/cake bake model Articles - -For more on bake and its code generation features please visit :doc:`/bake/usage`. - -Create the Articles Controller -============================== - -Next, we'll create a controller for our articles. The controller is -where all interaction with articles will happen. In a nutshell, it's the place -where you play with the business logic contained in the models and get work -related to articles done. We'll place this new controller in a file called -**ArticlesController.php** inside the **src/Controller** directory. Here's -what the basic controller should look like:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - class ArticlesController extends AppController - { - } - -Now, let's add an action to our controller. Actions often represent -a single function or interface in an application. For example, when -users request www.example.com/articles/index (which is also the same -as www.example.com/articles/), they might expect to see a listing of -articles. The code for that action would look like this:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - class ArticlesController extends AppController - { - public function index() - { - $articles = $this->Articles->find()->all(); - $this->set(compact('articles')); - } - } - -By defining function ``index()`` in our ``ArticlesController``, users can now -access the logic there by requesting www.example.com/articles/index. Similarly, -if we were to define a function called ``foobar()``, users would be able to -access that at www.example.com/articles/foobar. - -.. warning:: - - You may be tempted to name your controllers and actions a certain - way to obtain a certain URL. Resist that temptation. Follow - :doc:`/intro/conventions` (capitalization, plural names, etc.) and create - readable, understandable action names. You can map URLs to your code using - :doc:`/development/routing` covered later on. - -The single instruction in the action uses ``set()`` to pass resultset -from the controller to the view (which we'll create next). The ``find()`` method -of the ``ArticlesTable`` object returns an instance of ``Cake\\ORM\\Query`` and -calling its ``all()`` method returns as instance of ``Cake\\Collection\\CollectionInterface`` -which is set as a view variable called 'articles'. - -.. note:: - - If you completed :doc:`Part 1 of the Blog Tutorial - ` and created the ``articles`` table in - your Blog database you can leverage CakePHP's bake console and its code - generation capabilities to create the ArticlesController class:: - - bin/cake bake controller Articles - -For more on bake and its code generation features please visit :doc:`/bake/usage`. - -To learn more about CakePHP's controllers, check out the -:doc:`/controllers` chapter. - -Creating Article Views -====================== - -Now that we have our data flowing from our model, and our application -logic is defined by our controller, let's create a view for -the index action we created above. - -CakePHP views are just presentation-flavored fragments that fit inside -an application's layout. For most applications, they're HTML mixed -with PHP, but they may end up as XML, CSV, or even binary data. - -A layout is presentation code that is wrapped around a view. -Multiple layouts can be defined, and you can switch between -them, but for now, let's just use the default. - -Remember in the last section how we assigned the 'articles' variable -to the view using the ``set()`` method? That would hand down the query -object collection to the view to be invoked with a ``foreach`` iteration. - -CakePHP's template files are stored in **templates** inside a folder -named after the controller they correspond to (we'll have to create -a folder named 'Articles' in this case). To format this article data in a -nice table, our view code might look something like this: - -.. code-block:: php - - - -

Blog articles

- - - - - - - - - - - - - - - - -
IdTitleCreated
id ?> - Html->link($article->title, ['action' => 'view', $article->id]) ?> - - created->format(DATE_RFC850) ?> -
- -Hopefully this should look somewhat simple. - -You might have noticed the use of an object called ``$this->Html``. This is an -instance of the CakePHP :php:class:`Cake\\View\\Helper\\HtmlHelper` class. -CakePHP comes with a set of view helpers that make things like linking, form -output a snap. You can learn more about how to use them in -:doc:`/views/helpers`, but what's important to note here is that the ``link()`` -method will generate an HTML link with the given title (the first parameter) and -URL (the second parameter). - -When specifying URLs in CakePHP, it is recommended that you use the -array format. This is explained in more detail in the section on -Routes. Using the array format for URLs allows you to take -advantage of CakePHP's reverse routing capabilities. You can also -specify URLs relative to the base of the application in the form of -``/controller/action/param1/param2`` or use :ref:`named routes `. - -At this point, you should be able to point your browser to -http://www.example.com/articles/index. You should see your view, -correctly formatted with the title and table listing of the articles. - -If you happened to have clicked on one of the links we created in -this view (that link a article's title to a URL ``/articles/view/some\_id``), -you were probably informed by CakePHP that the action hasn't yet -been defined. If you were not so informed, either something has -gone wrong, or you actually did define it already, in which case -you are very sneaky. Otherwise, we'll create it in the -``ArticlesController`` now:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - class ArticlesController extends AppController - { - public function index() - { - $this->set('articles', $this->Articles->find()->all()); - } - - public function view($id = null) - { - $article = $this->Articles->get($id); - $this->set(compact('article')); - } - } - -The ``set()`` call should look familiar. Notice we're using -``get()`` rather than ``find()`` because we only really want -a single article's information. - -Notice that our view action takes a parameter: the ID of the article -we'd like to see. This parameter is handed to the action through -the requested URL. If a user requests ``/articles/view/3``, then the value -'3' is passed as ``$id``. - -We also do a bit of error checking to ensure a user is actually accessing -a record. By using the ``get()`` function in the Articles table, we make sure -the user has accessed a record that exists. In case the requested article is not -present in the database, or the id is false the ``get()`` function will throw -a ``NotFoundException``. - -Now let's create the view for our new 'view' action and place it in -**templates/Articles/view.php** - -.. code-block:: php - - - -

title) ?>

-

body) ?>

-

Created: created->format(DATE_RFC850) ?>

- -Verify that this is working by trying the links at ``/articles/index`` or -manually requesting an article by accessing ``/articles/view/{id}``, replacing -``{id}`` by an article 'id'. - -Adding Articles -=============== - -Reading from the database and showing us the articles is a great -start, but let's allow for the adding of new articles. - -First, start by creating an ``add()`` action in the -``ArticlesController``:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - use App\Controller\AppController; - - class ArticlesController extends AppController - { - public function initialize(): void - { - parent::initialize(); - - $this->loadComponent('Flash'); // Include the FlashComponent - } - - public function index() - { - $this->set('articles', $this->Articles->find()->all()); - } - - public function view($id) - { - $article = $this->Articles->get($id); - $this->set(compact('article')); - } - - public function add() - { - $article = $this->Articles->newEmptyEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - 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); - } - } - -.. note:: - - You need to include the :doc:`/controllers/components/flash` component in any controller - where you will use it. If necessary, include it in your ``AppController``. - -Here's what the ``add()`` action does: if the HTTP method of the -request was POST, try to save the data using the Articles model. If for some -reason it doesn't save, just render the view. This gives us a -chance to show the user validation errors or other warnings. - -Every CakePHP request includes a ``ServerRequest`` object which is accessible using -``$this->request``. The request object contains useful information regarding the -request that was just received, and can be used to control the flow of your -application. In this case, we use the :php:meth:`Cake\\Http\\ServerRequest::is()` -method to check that the request is a HTTP POST request. - -When a user uses a form to POST data to your application, that -information is available in ``$this->request->getData()``. You can use the -:php:func:`pr()` or :php:func:`debug()` functions to print it out if you want to -see what it looks like. - -We use FlashComponent's ``success()`` and ``error()`` methods to set a message -to a session variable. These methods are provided using PHP's `magic method -features `_. -Flash messages will be displayed on the page after redirection. In the layout we -have ``Flash->render() ?>`` which displays the message and clears the -corresponding session variable. The controller's -:php:meth:`Cake\\Controller\\Controller::redirect` function redirects to another -URL. The param ``['action' => 'index']`` translates to URL /articles i.e the -index action of the ``ArticlesController``. You can refer to -:php:func:`Cake\\Routing\\Router::url()` function on the `API -`_ to see the formats in which you can specify a URL for -various CakePHP functions. - -Calling the ``save()`` method will check for validation errors and -abort the save if any occur. We'll discuss how those errors are -handled in the following sections. - -Data Validation -=============== - -CakePHP goes a long way toward taking the monotony out of form input -validation. Everyone hates coding up endless forms and their -validation routines. CakePHP makes it easier and faster. - -To take advantage of the validation features, you'll need to use CakePHP's -:doc:`/views/helpers/form` helper in your views. The -:php:class:`Cake\\View\\Helper\\FormHelper` is available by default to all views -at ``$this->Form``. - -Here's our add view: - -.. code-block:: php - - - -

Add Article

- Form->create($article); - echo $this->Form->control('title'); - echo $this->Form->control('body', ['rows' => '3']); - echo $this->Form->button(__('Save Article')); - echo $this->Form->end(); - ?> - -We use the FormHelper to generate the opening tag for an HTML -form. Here's the HTML that ``$this->Form->create()`` generates: - -.. code-block:: html - -
- -If ``create()`` is called with no parameters supplied, it assumes -you are building a form that submits via POST to the current controller's -``add()`` action (or ``edit()`` action when ``id`` is included in -the form data). - -The ``$this->Form->control()`` method is used to create form elements -of the same name. The first parameter tells CakePHP which field -they correspond to, and the second parameter allows you to specify -a wide array of options - in this case, the number of rows for the -textarea. There's a bit of introspection and automagic here: -``control()`` will output different form elements based on the model -field specified. - -The ``$this->Form->end()`` call ends the form. Outputting hidden inputs if -CSRF/Form Tampering prevention is enabled. - -Now let's go back and update our **templates/Articles/index.php** -view to include a new "Add Article" link. Before the ````, add -the following line:: - - Html->link('Add Article', ['action' => 'add']) ?> - -You may be wondering: how do I tell CakePHP about my validation -requirements? Validation rules are defined in the model. Let's look -back at our Articles model and make a few adjustments:: - - // src/Model/Table/ArticlesTable.php - - namespace App\Model\Table; - - use Cake\ORM\Table; - use Cake\Validation\Validator; - - class ArticlesTable extends Table - { - public function initialize(array $config): void - { - $this->addBehavior('Timestamp'); - } - - public function validationDefault(Validator $validator): Validator - { - $validator - ->notEmptyString('title') - ->requirePresence('title', 'create') - ->notEmptyString('body') - ->requirePresence('body', 'create'); - - return $validator; - } - } - -The ``validationDefault()`` method tells CakePHP how to validate your data when -the ``save()`` method is called. Here, we've specified that both the body and -title fields must not be empty, and are required for both create and update -operations. CakePHP's validation engine is strong, with a number of pre-built -rules (credit card numbers, email addresses, etc.) and flexibility for adding -your own validation rules. For more information on that -setup, check the :doc:`/core-libraries/validation` documentation. - -Now that your validation rules are in place, use the app to try to add -an article with an empty title or body to see how it works. Since we've used the -:php:meth:`Cake\\View\\Helper\\FormHelper::control()` method of the FormHelper to -create our form elements, our validation error messages will be shown -automatically. - -Editing Articles -================ - -Post editing: here we go. You're a CakePHP pro by now, so you -should have picked up a pattern. Make the action, then the view. -Here's what the ``edit()`` action of the ``ArticlesController`` would look -like:: - - // src/Controller/ArticlesController.php - - public function edit($id = null) - { - $article = $this->Articles->get($id); - 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); - } - -This action first ensures that the user has tried to access an existing record. -If they haven't passed in an ``$id`` parameter, or the article does not -exist, we throw a ``NotFoundException`` for the CakePHP ErrorHandler to take -care of. - -Next the action checks whether the request is either a POST or a PUT request. If -it is, then we use the POST data to update our article entity by using the -``patchEntity()`` method. Finally we use the table object to save the entity -back or kick back and show the user validation errors. - -The edit view might look something like this: - -.. code-block:: php - - - -

Edit Article

- Form->create($article); - echo $this->Form->control('title'); - echo $this->Form->control('body', ['rows' => '3']); - echo $this->Form->button(__('Save Article')); - echo $this->Form->end(); - ?> - -This view outputs the edit form (with the values populated), along -with any necessary validation error messages. - -CakePHP will determine whether a ``save()`` generates an insert or an -update statement based on the state of the entity. - -You can now update your index view with links to edit specific -articles: - -.. code-block:: php - - - -

Blog articles

-

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

-
- - - - - - - - - - - - - - - - - - -
IdTitleCreatedAction
id ?> - Html->link($article->title, ['action' => 'view', $article->id]) ?> - - created->format(DATE_RFC850) ?> - - Html->link('Edit', ['action' => 'edit', $article->id]) ?> -
- -Deleting Articles -================= - -Next, let's make a way for users to delete articles. Start with a -``delete()`` action in the ``ArticlesController``:: - - // src/Controller/ArticlesController.php - - public function delete($id) - { - $this->request->allowMethod(['post', 'delete']); - - $article = $this->Articles->get($id); - if ($this->Articles->delete($article)) { - $this->Flash->success(__('The article with id: {0} has been deleted.', h($id))); - return $this->redirect(['action' => 'index']); - } - } - -This logic deletes the article specified by ``$id``, and uses -``$this->Flash->success()`` to show the user a confirmation -message after redirecting them on to ``/articles``. If the user attempts to -do a delete using a GET request, the ``allowMethod()`` will throw an Exception. -Uncaught exceptions are captured by CakePHP's exception handler, and a nice -error page is displayed. There are many built-in -:doc:`Exceptions ` that can be used to indicate the various -HTTP errors your application might need to generate. - -Because we're just executing some logic and redirecting, this -action has no view. You might want to update your index view with -links that allow users to delete articles, however: - -.. code-block:: php - - - -

Blog articles

-

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

- - - - - - - - - - - - - - - - - - - -
IdTitleCreatedActions
id ?> - Html->link($article->title, ['action' => 'view', $article->id]) ?> - - created->format(DATE_RFC850) ?> - - Form->postLink( - 'Delete', - ['action' => 'delete', $article->id], - ['confirm' => 'Are you sure?']) - ?> - Html->link('Edit', ['action' => 'edit', $article->id]) ?> -
- -Using :php:meth:`~Cake\\View\\Helper\\FormHelper::postLink()` will create a link -that uses JavaScript to do a POST request deleting our article. - -.. warning:: - - Allowing content to be deleted using GET requests is dangerous, as web - crawlers could accidentally delete all your content. - -.. note:: - - This view code also uses the ``FormHelper`` to prompt the user with a - JavaScript confirmation dialog before they attempt to delete an - article. - -Routes -====== - -For some, CakePHP's default routing works well enough. Developers -who are sensitive to user-friendliness and general search engine -compatibility will appreciate the way that CakePHP's URLs map to -specific actions. So we'll just make a quick change to routes in -this tutorial. - -For more information on advanced routing techniques, see -:ref:`routes-configuration`. - -By default, CakePHP responds to a request for the root of your site -(for example, http://www.example.com) using its ``PagesController``, rendering -a view called "home". Instead, we'll replace this with our -ArticlesController by creating a routing rule. - -CakePHP's routing is found in **config/routes.php**. You'll want -to comment out or remove the line that defines the default root -route. It looks like this: - -.. code-block:: php - - $builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); - -This line connects the URL '/' with the default CakePHP home page. -We want it to connect with our own controller, so replace that line -with this one: - -.. code-block:: php - - $builder->connect('/', ['controller' => 'Articles', 'action' => 'index']); - -This should connect users requesting '/' to the ``index()`` action of -our ``ArticlesController``. - -.. note:: - - CakePHP also makes use of 'reverse routing'. If, with the above - route defined, you pass - ``['controller' => 'Articles', 'action' => 'index']`` to a - function expecting an array, the resulting URL used will be '/'. - It's therefore a good idea to always use arrays for URLs as this - means your routes define where a URL goes, and also ensures that - links point to the same place. - -Conclusion -========== - -Keep in mind that this tutorial was very basic. CakePHP has *many* more -features to offer, and is flexible in ways we didn't wish to cover -here for simplicity's sake. Use the rest of this manual as a guide -for building more feature-rich applications. - -Now that you've created a basic CakePHP application, you can either continue to -:doc:`/tutorials-and-examples/blog/part-three`, or start your own project. You -can also peruse the :doc:`/topics` or `API `_ to -learn more about CakePHP. - -If you need help, there are many ways to get the help you need - please see the -:doc:`/intro/where-to-get-help` page. Welcome to CakePHP! - -Suggested Follow-up Reading ---------------------------- - -These are common tasks people learning CakePHP usually want to study next: - -1. :ref:`view-layouts`: Customizing your website layout -2. :ref:`view-elements`: Including and reusing view snippets -3. :doc:`/bake/usage`: Generating basic CRUD code -4. :doc:`/tutorials-and-examples/blog-auth-example/auth`: User authentication - and authorization tutorial - -.. meta:: - :title lang=en: Blog Tutorial Adding a Layer - :keywords lang=en: doc models,validation check,controller actions,model post,php class,model class,model object,business logic,database table,naming convention,bread and butter,callbacks,prefixes,nutshell,interaction,array,cakephp,interface,applications,delete diff --git a/en/tutorials-and-examples/cms/articles-controller.rst b/en/tutorials-and-examples/cms/articles-controller.rst index 06d30ad314..b52cd0de53 100644 --- a/en/tutorials-and-examples/cms/articles-controller.rst +++ b/en/tutorials-and-examples/cms/articles-controller.rst @@ -547,5 +547,15 @@ that uses JavaScript to do a POST request deleting our article. JavaScript confirmation dialog before they attempt to delete an article. -With a basic articles management setup, we'll create the :doc:`basic actions +.. tip:: + + The ``ArticlesController`` can also be built with ``bake``: + + .. code-block:: console + + /bin/cake bake controller articles + + However, this does not build the **templates/Articles/*.php** files. + +With a basic articles management setup, we'll create the :doc:`basic actions for our Tags and Users tables `. diff --git a/en/tutorials-and-examples/cms/articles-model.rst b/en/tutorials-and-examples/cms/articles-model.rst new file mode 100644 index 0000000000..d9fd3c2036 --- /dev/null +++ b/en/tutorials-and-examples/cms/articles-model.rst @@ -0,0 +1,85 @@ +CMS Tutorial - Creating our First Model +####################################### + +Models are the heart of CakePHP applications. They enable us to read and +modify our data. They allow us to build relations between our data, validate +data, and apply application rules. Models provide the foundation necessary to +create our controller actions and templates. + +CakePHP's models are composed of ``Table`` and ``Entity`` objects. ``Table`` +objects provide access to the collection of entities stored in a specific table. +They are stored in **src/Model/Table**. The file we'll be creating will be saved +to **src/Model/Table/ArticlesTable.php**. The completed file should look like +this:: + + addBehavior('Timestamp'); + } + } + +We've attached the :doc:`/orm/behaviors/timestamp` behavior, which will +automatically populate the ``created`` and ``modified`` columns of our table. +By naming our Table object ``ArticlesTable``, CakePHP can use naming conventions +to know that our model uses the ``articles`` table. CakePHP also uses +conventions to know that the ``id`` column is our table's primary key. + +.. note:: + + CakePHP will dynamically create a model object for you if it + cannot find a corresponding file in **src/Model/Table**. This also means + that if you accidentally name your file wrong (i.e. articlestable.php or + ArticleTable.php), CakePHP will not recognize any of your settings and will + use the generated model instead. + +We'll also create an Entity class for our Articles. Entities represent a single +record in the database and provide row-level behavior for our data. Our entity +will be saved to **src/Model/Entity/Article.php**. The completed file should +look like this:: + + true, + 'body' => true, + 'published' => true, + 'created' => true, + 'modified' => true, + 'users' => true, + ]; + } + +Right now, our entity is quite slim; we've only set up the ``_accessible`` +property, which controls how properties can be modified by +:ref:`entities-mass-assignment`. + +.. tip:: + The ``ArticlesTable`` and ``Article`` Entity classes can be generated from a + terminal: + + .. code-block:: console + + bin/cake bake model articles + +We can't do much with this model yet. Next, we'll create our first +:doc:`Controller and Template ` +to allow us to interact with our model. diff --git a/en/tutorials-and-examples/cms/database.rst b/en/tutorials-and-examples/cms/database.rst index 6cd6c912ea..7abbcf9a2f 100644 --- a/en/tutorials-and-examples/cms/database.rst +++ b/en/tutorials-and-examples/cms/database.rst @@ -110,7 +110,7 @@ following SQL instead: (1, 'First Post', 'first-post', 'This is the first post.', TRUE, NOW(), NOW()); -You may have noticed that the ``articles_tags`` table used a composite primary +You may have noticed that the ``articles_tags`` table uses a composite primary key. CakePHP supports composite primary keys almost everywhere, allowing you to have simpler schemas that don't require additional ``id`` columns. @@ -153,80 +153,58 @@ able to connect to the database' section has a green chef hat. The file **config/app_local.php** is a local override of the file **config/app.php** used to configure your development environment quickly. -Creating our First Model -======================== +Migrations +========== -Models are the heart of CakePHP applications. They enable us to read and -modify our data. They allow us to build relations between our data, validate -data, and apply application rules. Models provide the foundation necessary to -create our controller actions and templates. +The SQL statements to create the tables for this tutorial can also be generated +using the Migrations Plugin. Migrations provide a platform-independent way to +run queries so the subtle differences between MySQL, PostgreSQL, SQLite, etc. +don't become obstacles. -CakePHP's models are composed of ``Table`` and ``Entity`` objects. ``Table`` -objects provide access to the collection of entities stored in a specific table. -They are stored in **src/Model/Table**. The file we'll be creating will be saved -to **src/Model/Table/ArticlesTable.php**. The completed file should look like -this:: +.. code-block:: console - addColumn('article_id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('tag_id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); - class ArticlesTable extends Table - { - public function initialize(array $config): void - { - parent::initialize($config); - $this->addBehavior('Timestamp'); - } - } + Remove those lines to prevent foreign key problems. Once adjustments are + done:: -We've attached the :doc:`/orm/behaviors/timestamp` behavior, which will -automatically populate the ``created`` and ``modified`` columns of our table. -By naming our Table object ``ArticlesTable``, CakePHP can use naming conventions -to know that our model uses the ``articles`` table. CakePHP also uses -conventions to know that the ``id`` column is our table's primary key. + bin/cake migrations migrate -.. note:: +Likewise, the starter data records can be done with seeds. - CakePHP will dynamically create a model object for you if it - cannot find a corresponding file in **src/Model/Table**. This also means - that if you accidentally name your file wrong (i.e. articlestable.php or - ArticleTable.php), CakePHP will not recognize any of your settings and will - use the generated model instead. +.. code-block:: console -We'll also create an Entity class for our Articles. Entities represent a single -record in the database and provide row-level behavior for our data. Our entity -will be saved to **src/Model/Entity/Article.php**. The completed file should -look like this:: + bin/cake bake seed Users + bin/cake bake seed Articles - true, - 'body' => true, - 'published' => true, - 'created' => true, - 'modified' => true, - 'users' => true, - ]; - } - -Right now, our entity is quite slim; we've only set up the ``_accessible`` -property, which controls how properties can be modified by -:ref:`entities-mass-assignment`. - -We can't do much with our models yet. Next, we'll create our first -:doc:`Controller and Template ` to allow us to interact -with our model. +Fill the seed data above into the new ``UsersSeed`` and ``ArticlesSeed`` +classes, then:: + + bin/cake migrations seed + +Read more about building migrations and data seeding: `Migrations +`__ + +With the database built, we can now build :doc:`Models +`. diff --git a/en/tutorials-and-examples/cms/installation.rst b/en/tutorials-and-examples/cms/installation.rst index 25de250972..d7c7c3ab50 100644 --- a/en/tutorials-and-examples/cms/installation.rst +++ b/en/tutorials-and-examples/cms/installation.rst @@ -14,8 +14,7 @@ Here's what you'll need: ``pdo_mysql`` enabled in PHP. #. Basic PHP knowledge. -Before starting you should make sure that you have got an up to date PHP -version: +Before starting you should make sure that you're using a supported PHP version: .. code-block:: console @@ -65,12 +64,12 @@ There are other ways to install CakePHP. If you cannot or don't want to use Composer, check out the :doc:`/installation` section. Regardless of how you downloaded and installed CakePHP, once your set up is -completed, your directory setup should look something like the following:: +completed, your directory setup should look like the following, though other +files may also be present:: cms/ bin/ config/ - logs/ plugins/ resources/ src/ @@ -79,12 +78,8 @@ completed, your directory setup should look something like the following:: tmp/ vendor/ webroot/ - .editorconfig - .gitignore - .htaccess composer.json index.php - phpunit.xml.dist README.md Now might be a good time to learn a bit about how CakePHP's directory structure @@ -93,6 +88,13 @@ works: check out the :doc:`/intro/cakephp-folder-structure` section. If you get lost during this tutorial, you can see the finished result `on GitHub `_. +.. tip:: + + The ``bin/cake`` console utility can build most of the classes and data + tables in this tutorial automatically. However, we recommend following along + with the manual code examples to understand how the pieces fit together and + how to add your application logic. + Checking our Installation ========================= @@ -115,4 +117,4 @@ bullet points should be green chef hats other than CakePHP being able to connect your database. If not, you may need to install additional PHP extensions, or set directory permissions. -Next, we will build our :doc:`Database and create our first model `. +Next, we will build our :doc:`Database`.