📜 ⬆️ ⬇️

Still cake, part 3.0.0

Finally released in the release of CakePHP 3.0.0.

Our company uses in development including cakephp, as the main framework for backend development. For four years, we thoroughly figured out its advantages and disadvantages. And, of course, many of the problems that will disappear with the transition to 3.0 have already been solved, but the development of the tool used is good news. After this update, we expect that cakephp will regain its deserved popularity.



')
What's new in CakePHP 3.0.0:


I have already played enough with version 3.0, so let's take a little look at the capabilities of this framework.

Tutorial navigation menu:

CakePHP 3 Migrations - Quick Start
View Cells in CakePHP 3.0 - Quick Start
Events in CakePHP 3 - instructions in 4 steps
Twitter bootstrap forms in CakePHP 3
Changing the markup for page numbering in CakePHP 3
Topics in CakePHP 3 - Step by Step Guide


CakePHP 3 Migrations - Quick Start



In CakePHP, 3 migrations are a separate plugin and wrapper for Phinx libraries.

First, a little history. Each team in which I worked used certain version control systems to work with the code, among which the “favorite” was the Git system. However, if it came to changes in the synchronization of the structure of the database, many were puzzled. Using migration seemed like a miracle! :)

Step 1 - Install Migration Plugin

Install CakePHP and then edit the composer.json file to add the following:
"require": { "cakephp/migrations": "dev-master" } 

Start the update of the dependency manager and then load the plugin into your application by editing the bootstrap.php file and adding the following line:
 Plugin::load('Migrations'); 

That's all! You are now ready to create the first migration.

Step 2 - Creating the first migration

At the command prompt, enter

./bin/cake migrations create CreatePostsTable

and you will see

using migration path / home / tariquesani / www / migration / config / Migrations
created ./config/Migrations/20141015052852_create_posts_table.php

As you can see, a skeleton for migration was created for you. Open it in your favorite code editor, where it will look like
 <?php use Phinx\Migration\AbstractMigration; class CreatePostsTable extends AbstractMigration { /** * Change Method. * * More information on this method is available here: * http://docs.phinx.org/en/latest/migrations.html#the-change-method * * Uncomment this method if you would like to use it. * public function change() { } */ /** * Migrate Up. */ public function up() { } /** * Migrate Down. */ public function down() { } } 

Let's write something in function up () to create the Posts table. “Function up” is needed for everything that you want to add or change, developing a project, and “function down ()” is needed to reverse or roll back these changes. You can also use the new function Phinx change (), which is reversible, but now we will work only with "up" and "down". Edit these two functions to the form
  /** * Migrate Up. */ public function up() { $posts = $this->table('posts'); $posts->create(); } /** * Migrate Down. */ public function down() { $this->dropTable('posts'); } 


Step 3 - Run the created migration

To start the migration, enter

./bin/cake migrations migrate

You will see

Welcome to CakePHP v3.0.0-beta2 Console
- App: src
Path: / home / tariquesani / www / migration / src /
- using migration path / home / tariquesani / www / migration / config / Migrations
using environment default
using adapter mysql
using database caketest

== 20141015052852 CreatePostsTable: migrating
== 20141015052852 CreatePostsTable: migrated 0.2205s

If you look into the database, there will definitely be a Posts table with an auto-increment ID field. We could add the necessary columns in our first migration, however since we did not do this, let's change this table to create another migration.

Step 3 - Create another migration table

Execute

./bin/cake migrations create AlterPostsTable

Edit the Change New Migration function by adding the following code there. Please note: we do not write anything in down ().
  public function change() { $posts = $this->table('posts'); $posts->addColumn('title', 'string') ->addColumn('body', 'text') ->addColumn('created', 'datetime') ->addColumn('modified', 'datetime') ->save(); } 

Restart the migration using the command above. Look at the structure of the Posts table in the database, and you will see that the corresponding fields appear there.

Step 4 - Attempt Rollback

Since we used the Change function, let's try to undo this change.

./bin/cake migrations rollback

This will remove all fields added to the AlterPostsTable migration table. You can roll back one migration at a time. If you run now

./bin/cake migrations status

then you will see

Status Migration ID Migration Name
- up 20141015052852 CreatePostsTable
down 20141015060152 AlterPostsTable

Also in the database you can confirm that these columns have been deleted. Another migration will simply return the columns back. Warning! Do not roll back if there are important data in the table, as they will be deleted. But you already knew that, right?

You can also execute SQL queries directly in the migration files (as described in the Phinx documentation). There is also talk that the possibility of the initial import of the structure from the existing database will be realized, which will be very good for the implementation of projects using Migrations. Since migrations are text files, they can be saved in VCS format and synchronized with all developers.


View Cells in CakePHP 3.0 - Quick Start



The concept of View Cells in CakePHP 3 was developed for a long time. Book View View Cells: mini-controllers with which you can view class logic and display templates. In 2007, Andy Dawson wrote about them. If I had to tell you where View Cells are used, I would say: “Think Widgets” - if you work with WordPress, you understand what I mean. In CakePHP, it was always possible to requestAction (), but this caused processor overload. Over time, we tried various options, including instantiating the controller, creating helpers, but it was always difficult to use them. View Cells has two undeniable advantages: small size and modularity.

Let's try using View Cell in a new application. I assume that you can install CakePHP 3 and create an application for Publishing. In the database table there should be a Title field, except for the obligatory ID column, but you set the rest of the fields as you see fit.

Now I will show you how to create a Latest Publications widget using View Cells.

Step 1

After you have created and verified the operation of the Publication application, re-open the terminal and create a View Cell using the following command

cake bake cell Posts

This command will create the files src / View / Cell / PostsCell.php and src / Template / Cell / Posts / display.ctp, as well as a file for writing tests. The PostsCell.php file is a class equivalent to a mini-controller, and the display.ctp file is a template file for displaying the PostsCell class method. More on this later.

Step 2

Open the PostsCell.php file in your favorite editor. It will look something like this.
 <?php namespace App\View\Cell; use Cake\View\Cell; /** * Posts cell */ class PostsCell extends Cell { /** * List of valid options that can be passed into this * cell's constructor. * * @var array */ protected $_validCellOptions = []; /** * Default display method. * * @return void */ public function display() { #add code } } 

You will see that the module classes follow certain rules.
• modules are in App \ View \ Cell
• class names end with Cell
• Classes inherit from Cake \ View \ Cell.

This creates an empty display () method. This is the default method that is called when the module is displayed. Let's enter the code in the display method. We are going to create the Latest Publications widget, the following code will do for this purpose.
  public function display() { $this->loadModel('Posts'); $total_posts = $this->Posts->find()->count(); $recent_posts = $this->Posts->find() ->select('title') ->order(['created' => 'DESC']) ->limit(3) ->toArray(); $this->set(['total_posts' => $total_posts, 'recent_posts' => $recent_posts]); } 

This code counts the number of records in view of the last three publications and builds them for display.

Step 3

Open the src / Template / Cell / Posts / display.ctp file and copy the code there
 <div class="actions"> <h3><?= __('Recent Posts') ?></h3> <ul> <li><strong>You have <?= $total_posts ?> posts total</strong></li> <?php foreach ($recent_posts as $post) { echo "<li>".$post['title']."</li>"; } ?> </ul> </div> 

The code is pretty simple, so I will not bore you with explanations. We are almost done ... One more step!

Step 4

The last step is to display the module as we need it. I want to display this widget on every page, so I will use the default solution. Open the src / Template / Layout / default.ctp file and copy the two lines of code under the $ this-> Flash-> render () line.
 <?php $cell = $this->cell('Posts'); ?> <?= $cell ?> 

The first line loads and starts the display method, and the second line displays the module. That's all! Restart your application, and you will see that you have something similar to the image from the screenshot. Go to different pages - the widget will still be in the left column. :)

Conclusion

View Cells is a flexible and powerful tool that is now built directly into CakePHP. Of course, using it you can perform much more interesting tasks than creating widgets. I highly recommend reading book.cakephp.org/3.0/en/views/cells.html in order to fully understand the benefits of this tool.


Events in CakePHP 3 - instructions in 4 steps



In CakePHP, the Events system appeared from version 2.1, and in version 3.0, this will change the principles of markup created almost a year ago. Events system in CakePHP is very similar to the “Observer” template. I think that you are already familiar with the system of events in CakePHP in the form in which it was introduced in version 2.x, but if not, read this wonderful article by Martin Bean.

Let's look at an example of a very simple scenario where you will manage the event while saving the Publication. The listener class responds to this event and writes data about it to the log file. Of course, this is not God knows what serious action, but this is just an example to start working with the Events system in CakePHP 3. In fact, classes-students take a lot more work. If you combine Events with something like Gearman, you can get a really powerful and scalable app.

Step 1

Install CakePHP 3 and create an MVC template for Publishing. The appearance of your table for Publications is absolutely not important, since we will work with the ID field and the class listener.

Step 2

Open the PostsTable.php file that is located in src / Model / Table and copy the following code into the afterSave method
  public function afterSave($created, $options = array()) { if ($created) { $event = new Event('Model.Post.created', $this, $options); $this->eventManager()->dispatch($event); } } 

This code is written in plain English. It says that when creating a publication, a new Event is created called “Model.Post.created” and sent using the eventManager. You can name it as you please, but it is better to follow the principles of the name "Layer. Object. Verb". (it was “You can call it at any time,” the original is “You can call your event whatever you want”)

Step 3

Create a class listener. The listener class is an implementation of the Cake \ Event \ EventListener interface. Classes-listeners that implement this interface should work with the observance of the implementedEvents () method. This method returns an associative array with the names of all events that are handled by the class. I want to keep my listener in the Event folder at the same level as the Model, Controller, etc. I set the App \ Event namespace, so it can be automatically loaded via PSR-4. Create a file called PostListener.php in the Event folder with the following code
 namespace App\Event; use Cake\Log\Log; use Cake\Event\EventListener; class PostListener implements EventListener { public function implementedEvents() { return array( 'Model.Post.created' => 'updatePostLog', ); } public function updatePostLog($event, $entity, $options) { Log::write( 'info', 'A new post was published with id: ' . $event->data['id']); } } 

ImplementedEvents tells the system which method to invoke when a specific event occurs. In our case, at the Model.Post.created event, the updatePostLog method will be called. The updatePostLog method simply writes a message to the logs / debug.log file.

Step 4

Finally, we have to register this class listener. To do this, we will use the available EventManager. Paste the following code at the end of the config / bootstrap.php file
 use App\Event\PostListener; $PostListener = new PostListener(); use Cake\Event\EventManager; EventManager::instance()->attach($PostListener); 

That's all! Now try to create a publication or several publications and in the debug.log file will appear

2014-08-27 04:48:57 Info: A new post was published with id: 14
2014-08-27 05:34:27 Info: A new post was published with id: 15
2014-08-28 04:44:41 Info: A new post was published with id: 16

You now have a basic event system. Here you can see the user manual and find out what else you can do with this system - book.cakephp.org/3.0/en/core-libraries/events.html .


Twitter bootstrap forms in CakePHP 3



Many questions have been about creating Twitter bootstrap forms in CakePHP 3. Discussions have been around different approaches.

Do you just need to change the form templates? Do I need to create widgets? Or do we need our own helper form? In short, it all depends on what customization features you need. I will move on to specific situations later, and now we will figure out what we need to do.

We will create a text box for entering data in the form

And when an error occurs, it will look like this

The HTML code for this field looks like this.
 <div class="form-group"> <label for="title">Title</label> <div class="input-group"> <span class="input-group-addon">@</span> <input type="text" name="title" class="form-control" required="required" maxlength="50" id="title"> </div> </div> 

Helper form syntax we will use
 echo $this->Form->input('title', ['addon'=>'@', "class"=>"form-control"]); 

In this approach, I used a combination of form-file files and the creation of a widget that overwrites the main form of the widget.

Step 1

Create a file called form-templates.php in src / Config / folder.

Step 2

Put the following code in the form-templates.php file (do not forget to add the opening php tag)
 $config = [ 'addoninput' => '<div class="input-group"><span class="input-group-addon">{{addon}}</span><input type="{{type}}" name="{{name}}"{{attrs}}></div>', 'inputContainer' => '<div class="form-group">{{content}}</div>', 'inputContainerError' => '<div class="form-group has-error has-feedback {{type}}{{required}}">{{content}}{{error}}</div>', 'error' => '<div class="alert alert-danger" >{{content}}</div>', ]; 

Here I have defined an additional template called addoninput with additional characters to use. I also redefined inputContainer, inputContainerError, and error patterns for the best features of bootstrap.

Step 3

Two steps would be enough if I didn’t want to get the extension, but I want to get it, how it looks great and is visually useful for users. For the normal operation of the extension, I need to override the Main widget. Copy Basic.php from cakephp / src / View / Widget to app / src / View / Widget. The only difference is the visualization method, which now looks
  public function render(array $data, ContextInterface $context) { $data += [ 'name' => '', 'val' => null, 'type' => null, 'escape' => true, ]; $data['value'] = $data['val']; $addon = ''; $template = 'input'; if(isset($data['addon'])) { $addon = $data['addon']; $template = 'addoninput'; } unset($data['val'], $data['addon']); return $this->_templates->format( $template, [ 'name' => $data['name'], 'addon'=> $addon, 'attrs' => $this->_templates->formatAttributes( $data, ['name', 'type'] ), ]); } 

Since this was an experiment, I just needed to do a few checks in $ data and change the template to addoninput.

Step 4

Finally, let the application learn about widgets and form templates by adding the following code to the application controller
 public $helpers = [ 'Form' => [ 'widgets' => [ '_default' => ['App\View\Widget\Basic'], ], 'templates' => 'form-templates.php', ] ]; 

That's all! Some may say that the process was simpler in CakePHP 2.x, but it seems to me that in this version the code is much cleaner.

Basic rules for using custom templates, widgets and form helpers:



Changing the markup for page numbering in CakePHP 3



Yes, in CakePHP 2.x it was possible to change the markup for page numbering, but for this it was necessary to mark up in the parameters array to the helper number method for page numbering. Thus, in the end, it turned out something like

echo $ this-> Paginator-> numbers (array ('before' => ''));

It worked, but was confused and often led to errors.

CakePHP 3 has an elegant solution to this problem in the form of PaginatorHelper templates. PaginatorHelper templates make it easy to separate markup from code, and the code follows DRY principles. Let's try to apply them. So here we will change the pagination section from such



In such



Step 1

Create a file called paginator-templates.php in the src / Config / folder.

Step 2

Put the following code in paginator-templates.php (do not forget about the opening php tag)
 $config = [ 'number' => '<option>{{text}}</option>', 'current' => '<option selected >{{text}}</option>', ]; 


Step 3

Open the numbered template, for example: Posts / index.ctp from the previous blog entry and replace the following lines
 echo $this->Paginator->prev('< ' . __('previous')); echo $this->Paginator->numbers(); echo $this->Paginator->next(__('next') . ' >'); 

on
 <form method="get"> <label for="pageselect">Page number</label> <select name='page' id='pageselect'> <?= $this->Paginator->numbers(); ?> </select> <button type="submit">Go</button> </form> 

The important line in the above code snippet is $ this-> Paginator-> numbers (); all the rest is a simple form that uses the GET method. I could use a form helper to create the beginning and end of the form, but here it doesn't make much sense.

Step 4

Add the following code to the controller
  public $helpers = [ 'Paginator' => ['templates' => 'paginator-templates'] ]; 

That's all! After reloading the page you will see a form to go to any page after clicking on the “Go” button. Yes, I know that this form is different in appearance from the image above, but there simply Bootstrap CSS is applied to the elements of the form, which you can figure out on your own.

Additionally

Here we changed the pagination markup for the entire application. We can do this for a single theme by loading templates from plugins.
 public $helpers = [ 'Paginator' => ['templates' => 'Twit.paginator-templates'] ]; 

The pagination directive in the application controller will not work, because the plugin's application controller is not executed if the plugin is running as a theme.

Templates can be changed during execution, but in this case the purity of the code is broken.

Below are the available default templates, so you can change from depending on your needs and preferences.
 $config = [ 'nextActive' => '<li class="next"><a rel="next" href="{{url}}">{{text}}</a></li>', 'nextDisabled' => '<li class="next disabled"><span>{{text}}</span></li>', 'prevActive' => '<li class="prev"><a rel="prev" href="{{url}}">{{text}}</a></li>', 'prevDisabled' => '<li class="prev disabled"><span>{{text}}</span></li>', 'counterRange' => '{{start}} - {{end}} of {{count}}', 'counterPages' => '{{page}} of {{pages}}', 'first' => '<li class="first"><a href="{{url}}">{{text}}</a></li>', 'last' => '<li class="last"><a href="{{url}}">{{text}}</a></li>', 'number' => '<li><a href="{{url}}">{{text}}</a></li>', 'current' => '<li class="active"><span>{{text}}</span></li>', 'ellipsis' => '<li class="ellipsis">...</li>', 'sort' => '<a href="{{url}}">{{text}}</a>', 'sortAsc' => '<a class="asc" href="{{url}}">{{text}}</a>', 'sortDesc' => '<a class="desc" href="{{url}}">{{text}}</a>', 'sortAscLocked' => '<a class="asc locked" href="{{url}}">{{text}}</a>', 'sortDescLocked' => '<a class="desc locked" href="{{url}}">{{text}}</a>', ]; 



Topics in CakePHP 3 - Step by Step Guide



Almost every application that I developed had themes that could be modified by both the end user and the administrator, and these themes could be changed on the fly. In CakePHP 2.x, doing this was easy. Even after the first alpha release of CakePHP 3.x, I wanted to try, as in version 3, work with themes was implemented.
I want to show how to turn it


Default theme

in it


The Twitter bootstrap theme we will create

I think you know how to install CakePHP 3 and know how to create Model, Controller and associations.

Step 0

Before you start, create a table of publications using SQL type
 -- -- Table structure for table `posts` -- CREATE TABLE IF NOT EXISTS `posts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(50), `body` text, `created` datetime, `modified` datetime, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ; -- -- Dumping data for table `posts` -- INSERT INTO `posts` (`id`, `title`, `body`, `created`, `modified`) VALUES (1, 'The title', 'This is the post body.', '2014-02-28 14:15:57', '2014-07-19 11:02:12'), (2, 'A title once again', 'And the post body follows.', '2014-02-28 14:15:57', '2014-07-19 11:02:19'), (3, 'Title strikes back', 'This is really exciting! Not.', '2014-02-28 14:15:57', '2014-07-19 11:02:24'), (5, 'This is a new post', 'Content of new post', '2014-07-19 04:41:18', '2014-07-19 04:41:18'); 


Step 1

Create a Publication Model, Publication Controller, and Display. At this stage, if you go to yourapp / posts, you can see a screen similar to the first screenshot.

Step 2

Themes in CakePHP 3 are full-featured plugins, so create a plugin called “Twit” by typing the following command on the command line

Console / cake bake plugin twit

Step 3

We are going to create a theme called Twitter Bootstrap. To do this, download the Bootstrap distribution, unpack it and copy the CSS, fonts and JS folder into plugins / Twit / webroot /. : , , - .

4

, CakePHP 3 . plugins/Twit/src/Template, plugins/Twit/src/Template src/Template/ , , plugins/Twit/src/Template/Layout. default.ctp . , - 10-23 54-58, – Twitter Bootstrap.
 <?php $cakeDescription = 'CakePHP: the rapid development php framework'; ?> <!DOCTYPE html> <html lang="en"> <head> <?= $this->Html->charset(); ?> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title> <?= $cakeDescription; ?>: <?= $this->fetch('title'); ?> </title> <?php echo $this->Html->meta('icon'); echo $this->Html->css('bootstrap.min.css'); echo $this->Html->css('starter-template.css'); echo $this->fetch('meta'); echo $this->fetch('css'); echo $this->fetch('script'); ?> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <div class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Project name</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Home</a></li> <li><a href="#about">About</a></li> <li><a href="#contact">Contact</a></li> </ul> </div><!--/.nav-collapse --> </div> </div> <div class="container"> <h1><?= $this->Html->link($cakeDescription, 'http://cakephp.org'); ?></h1> <?= $this->Flash->render(); ?> <?= $this->fetch('content'); ?> </div> </body> </html> 


5

index.ctp plugins/Twit/src/Template/Posts
 <div class="row"> <div class="col-md-8"> <h2><?= __('Posts'); ?></h2> <table class="table table-bordered"> <tr> <th><?= $this->Paginator->sort('id'); ?></th> <th><?= $this->Paginator->sort('title'); ?></th> <th><?= $this->Paginator->sort('body'); ?></th> <th><?= $this->Paginator->sort('created'); ?></th> <th class="actions"><?= __('Actions'); ?></th> </tr> <?php foreach ($posts as $post): ?> <tr> <td><?= h($post->id); ?> </td> <td><?= h($post->title); ?> </td> <td><?= h($post->body); ?> </td> <td><?= h($post->created); ?> </td> <td class="actions"> <?= $this->Html->link(__('View'), ['action' => 'view', $post->id], ['class' => 'btn btn-sm btn-default']); ?> <?= $this->Html->link(__('Edit'), ['action' => 'edit', $post->id], ['class' => 'btn btn-sm btn-info']); ?> <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $post->id], ['confirm' => __('Are you sure you want to delete # %s?', $post->id), 'class' => 'btn btn-sm btn-danger']); ?> </td> </tr> <?php endforeach; ?> </table> <p><?= $this->Paginator->counter(); ?></p> <ul class="pagination"> <?php echo $this->Paginator->prev('< ' . __('previous')); echo $this->Paginator->numbers(); echo $this->Paginator->next(__('next') . ' >'); ?> </ul> </div> <div class="col-md-4"> <h3><?= __('Actions'); ?></h3> <?= $this->Html->link(__('New Post'), ['action' => 'add'], ['class' => 'btn btn-default']); ?> </div> </div> 


6

AppController.php

public $theme = 'Twit';

7

.

findings



CakePHP . , , , . , : , , , .

:
CakePHP 3 –
View Cells CakePHP 3.0 –
Events CakePHP 3 – 4
Twitter bootstrap CakePHP 3
CakePHP 3
CakePHP 3 –

PS Vircities IlkFinKom. , .

Source: https://habr.com/ru/post/254489/


All Articles