CakePHP 3

CakePHP 1.3.0 released on April 24, 2010

CakePHP 3.0.0 released on March 22, 2015

Time to upgrade?

Cake3

What's changed?

Requirements

  • CakePHP 3.x supports PHP Version 5.4.16 and above (namespaces, traits, closures, anonymous functions, short syntax for array() [])

  • CakePHP 3.x requires the mbstring extension

  • CakePHP 3.x requires the intl extension. (Internationalization extension http://php.net/manual/en/book.intl.php)

Recommended

CakePHP should be installed with Composer

Cake3\WhatsChanged

Namespaces

In the PHP world, namespaces are designed to solve two problems:

  1. Name collisions between code you create, and internal PHP classes/functions/constants or third-party classes/functions/constants.
     
  2. Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving readability of source code
Cake3\WhatsChanged\Namespaces
<?php

namespace Sport\Football;

use Sport\Team;
use Sport\Kit\Shirt;
use Sport\Kit\Shorts;

class Bologna extends Team
{
    protected $shirt = null;

    protected $shorts = null;

    public function __construct()
    {
        $this->shirt = new Shirt(['red', 'blue']);
        $this->shorts = new Shorts(['white']);
        // I can also do
        // new \Sport\Kit\Shirt(['red', 'blue']);
    }
}


// or class Bologna extends \Sport\Team
Cake3\WhatsChanged\Namespaces
<?php

namespace Country\Italy\City;

use Sport\Football\Bologna as Bfc;

class Bologna
{
    public $footballTeam = null;
    
    public function __construct()
    {
        $this->footballTeam = new Bfc();
    }
}
Cake3\WhatsChanged\Namespaces
Cake3\WhatsChanged\Traits

Traits

Traits are a mechanism for code reuse in single inheritance languages such as PHP.

 

A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

 

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own.

<?php

namespace Log;

use Log\Log;

trait LogTrait
{
    public function log($msg)
    {
        return Log::write($msg);
    }
}
Cake3\WhatsChanged\Traits
<?php

namespace Country\Italy\City;

use Sport\Football\Bologna as Bfc;
use Log\LogTrait;

class Bologna
{
    use LogTrait;

    public $footballTeam = null;
    
    public function __construct()
    {
        $this->footballTeam = new Bfc();
        $this->log('Bfc created');
    }
}
// --------- example 1 ---------------
$greeting = function () {
  return 'Hello world';
}

// Call function
echo $greeting(); // Returns 'Hello world'

// --------- example 2 ---------------
// Create a user
$user = 'Bato';
 
// Create a Closure
$greeting = function () use ($user) {
  echo "Hello $user";
};
 
// Greet the user
$greeting(); // Returns 'Hello Bato'

// --------- example 3 ---------------
// Set a multiplier
$multiplier = 3;
 
// Create a list of numbers
$numbers = [1,2,3,4];
 
// Use array_walk to iterate through the list and multiply
array_walk($numbers, function ($number) use ($multiplier) {
  echo $number * $multiplier;
});
Cake3\WhatsChanged\Closures
Cake3\WhatsChanged\Composer

Composer

Dependency Manager for PHP.

https://getcomposer.org
http://packagist.org (Packagist is the main Composer repository)

 

Cake3\WhatsChanged\Composer

Dependency Manager

Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you.

 

Composer is not a package manager. Yes, it deals with "packages" or libraries, but it manages them on a per-project basis, installing them in a directory (e.g. vendor) inside your project. By default it will never install anything globally. Thus, it is a dependency manager.

Cake3\WhatsChanged\Composer

Declaring dependencies

All you need to do is create a composer.json file which describes the project's dependencies.

{
    "require": {
        "cakephp/cakephp": "~3.0"
    }
}

It requires cakephp >= 3.0 < 4.0.0

To resolve and download dependencies, run the install command

$ composer install

All dependencies are located in vendor directory.

Cake3\WhatsChanged\Composer

Declaring dependencies

After installing the dependencies, Composer writes the list of the exact versions it installed into a composer.lock file.

This locks the project to those specific versions.
The install command checks if a lock file is present, and if it is, it downloads the versions specified there (regardless of what composer.json says).

This means that if any of the dependencies get a new version, you won't get the updates automatically. To update to the new version, use the update command.

$ composer update

This will fetch the latest matching versions (according to your composer.json file) and also update the lock file with the new version.

Cake3\WhatsChanged\Composer

Autoloading

Composer also prepares an autoload file that's capable of autoloading all of the classes in any of the libraries that it downloads.

You can even add your own code to the autoloader by adding an autoload field to composer.json

require 'vendor/autoload.php';
{
    "autoload": {
        "psr-4": {"Acme\\": "src/"}
    }
}
  • PSR-0 autoloading
  • PSR-4 autoloading (recommended since it offers greater ease of use, no need to regenerate the autoloader when you add classes)
  • classmap generation
  • files includes

Composer will register a PSR-4 autoloader for the Acme namespace.

Cake3\WhatsChanged\Composer

composer.json of cakephp/app

{
    "name": "cakephp/app",
    "description": "CakePHP skeleton app",
    "homepage": "http://cakephp.org",
    "type": "project",
    "license": "MIT",
    "require": {
        "php": ">=5.4.16",
        "cakephp/cakephp": "~3.0",
        "mobiledetect/mobiledetectlib": "2.*",
        "cakephp/migrations": "~1.0",
        "cakephp/plugin-installer": "*"
    },
    "require-dev": {
        "psy/psysh": "@stable",
        "cakephp/debug_kit": "~3.0",
        "cakephp/bake": "~1.0"
    },
    "suggest": {
        "phpunit/phpunit": "Allows automated tests to be run without system-wide install.",
        "cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP."
    },
    "autoload": {
        "psr-4": {
            "App\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Test\\": "tests",
            "Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
        }
    },
    "scripts": {
        "post-install-cmd": "App\\Console\\Installer::postInstall",
        "post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump"
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}
Cake3\WhatsChanged\Composer

Create new CakePHP app

$ composer create-project --prefer-dist cakephp/app myapp

It creates a new CakePHP app based on cakephp/app skeleton  adding all required dependencies

Cake3\WhatsChanged\Conventions

Conventions

  • CakePHP 3 adopts PSR-2 coding style conventions
  • filenames match the class names, and follow the PSR-0 or PSR-4 standards for autoloading (PagesController => PagesController.php, ...)
  •  Table names corresponding to CakePHP models are plural and underscored
  • Field names with two or more words are underscored: first_name
  • Foreign keys in hasMany, belongsTo or hasOne relationships are recognized by default as the (singular) name of the related table followed by _id
  • Join tables, used in BelongsToMany relationships between models, should be named after the model tables they will join, arranged in alphabetical order (apples_zebras rather than zebras_apples)
  • View template files are named after the controller functions they display, in an underscored form
  • By convention traits in CakePHP are suffixed with Trait so they are easily discernible from classes or interfaces
Cake3\WhatsChanged\ApplicationDirectoryLayout

Application directory layout

Refer to https://github.com/cakephp/app


The application directory layout has changed and now follows PSR-4. (http://www.php-fig.org/psr/psr-4/)

\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>

 

  1. The fully qualified class name MUST have a top-level namespace name, also known as a "vendor namespace"
  2. The fully qualified class name MAY have one or more sub-namespace names (corresponding to folders name and in which the namespace separators represent directory separators).
  3. The fully qualified class name MUST have a terminating class name.

 

For example src/Cache/Cache.php is namespaced as Cake\Cache\Cache

Cake3\WhatsChanged\ApplicationDirectoryLayout
app/
├── bin
├── config
├── logs
├── plugins
├── src
├── tests
├── tmp
├── vendor
├── webroot
├── composer.json
├── index.php
├── phpunit.xml.dist
└── README.md

 

holds the Cake console executables

holds the Configuration files

holds logs

holds the Cake plugins

is where your application’s files will be placed

holds temp files

holds the test cases

holds the app dependencies

is the public document root

Cake3\WhatsChanged\ApplicationDirectoryLayout
src
├── Console
├── Controller
│   └── Component
├── Locale
├── Model
│   ├── Behavior
│   ├── Entity
│   └── Table
├── Shell
├── Template
│   ├── Element
│   │   └── Flash
│   ├── Email
│   │   ├── html
│   │   └── text
│   ├── Error
│   ├── Layout
│   │   ├── Email
│   │   │   ├── html
│   │   │   └── text
│   │   └── rss
│   └── Pages
└── View
    └── Helper
Cake3\WhatsChanged\ORM

ORM

CakePHP 3.0 features a new ORM that has been re-written from the ground up.

The new ORM solves several problems by having more specialized and focused classes.

In the past you would use Model and a Datasource for all operations.

Now the ORM is split into more layers:

  • Cake\Database\Connection - Provides a platform independent way to create and use connections
  • Cake\Database\Dialect - The classes in this namespace provide platform specific SQL
  • Cake\Database\Type - Is the gateway class to CakePHP database type conversion system
  • Cake\ORM\Table - The main entry point into the new ORM. Provides access to a single table
  • Cake\ORM\Behavior - The base class for behaviors
  • Cake\ORM\Query - A fluent object based query builder
  • Cake\ORM\ResultSet - A collection of results that gives powerful tools for manipulating data in aggregate
  • Cake\ORM\Entity - Represents a single row result.
Cake3\WhatsChanged\ORM\Table

Table

They handle many of the tasks that Model did in previous releases. Table classes handle tasks like

  • Creating queries.
  • Providing finders.
  • Validating and saving entities.
  • Deleting entities.
  • Defining & accessing associations.
  • Triggering callback events.
  • Interacting with behaviors.

Table classes have a pluralized name i.e. ArticlesTable class refers to articles db table

Cake3\WhatsChanged\ORM\Entity

Entity

In previous versions of CakePHP the Model class returned dumb arrays that could not contain any logic or behavior.

 

For CakePHP 3.0, the ORM always returns object result sets unless you explicitly disable that feature.

 

Entities are created in one of two ways. Either by loading data from the database, or converting request data into entities.

 

Once created, entities allow you to manipulate the data they contain and persist their data by collaborating with table objects.

Cake3\WhatsChanged\ORM

Find returns a Query Object

Calling find on a table will not return the results immediately, but will return a Query object

  • It is possible to alter queries further, after calling find


     
  • It is possible to stack custom finders to append conditions, sorting, limit and any other clause to the same query before it is executed
     
$articles = TableRegistry::get('Articles');
$query = $articles->find();
$query->where(['author_id' => 1])->order(['title' => 'DESC']);
$query = $articles->find('approved')->find('popular');
$query->find('latest');
Cake3\WhatsChanged\ORM
  • You can compose queries one into the other to create subqueries easier than ever

     
  • You can decorate queries with iterators and call methods without even touching the database
$query = $articles->find('approved');
$favoritesQuery = $article->find('favorites', ['for' => $user]);
$query->where(['id' => $favoritesQuery->select(['id'])]);
// No queries made in this example!
$results = $articles->find()
    ->order(['title' => 'DESC'])
    ->formatResults(function ($results) {
        return $results->extract('title');
    });

Queries can be seen as the result object, trying to iterate the query, calling toArray() or any method inherited from collection, will result in the query being executed and results returned to you.

Cake3\WhatsChanged\ORM
// 1.3
$article = $this->Article->find('first');

// 3.0
$article = $this->Articles->find()->first();

// 1.3
$article = $this->Article->find('first', array(
    'conditions' => array('author_id' => 1)
));

// 3.0
$article = $this->Articles->find('all', [
    'conditions' => ['author_id' => 1]
])->first();

// Can also be written
$article = $this->Articles->find()
    ->where(['author_id' => 1])
    ->first();


// This works in both CakePHP 1.3 and 3.0 but in 1.3 you have to use array() syntax
$articles = $this->Articles->find('all', [
    'fields' => ['id', 'title'],
    'conditions' => [
        'OR' => ['title' => 'Cake', 'author_id' => 1],
        'published' => true
    ],
    'contain' => ['Authors'], // The only change! (notice plural)
    'order' => ['title' => 'DESC'],
    'limit' => 10,
]);

Examples 1.3 vs 3.0

Cake3\WhatSChanged\ORM

Recursive and ContainableBehavior Removed

In CakePHP 3.0 ContainableBehavior, recursive, bindModel, and unbindModel have all been removed.

Instead the contain() method has been promoted to be a core feature of the query builder.

Associations are only loaded if they are explicitly turned on.

// To only load data from the articles table
$query = $this->Articles->find('all');


// To load articles and their related authors you would do:
$query = $this->Articles->find('all')->contain(['Authors']);
Cake3\WhatsChanged\ORM

Other differences

  • No afterFind callback. Replaced by formatResult and Map/Reduce
  • Associations No Longer Defined as Properties
  • hasAndBelongsToMany has been renamed to belongsToMany
  • Validation No Longer Defined as a Property
  • Support for composite primary keys.

What's new?

  • Event System

  • Request & Response objects

  • Exception handling

  • View cells

  • Interactive Console (REPL Read Eval Print Loop)

  • PHP Built in server

Cake3\WhatsNew

Request & Response objects

Cake3\WhatsNew\Network

The request and response objects provide an abstraction around HTTP requests and responses. 

Request

 On each request one Request is created and then passed by reference to the various layers of an application that use request data

By default is availabe in:

  • Controllers
  • Views
  • Helpers
  • Cells
  • Components (through Controllers)
$this->request
Cake3\WhatsNew\Network\Request

In general it contains all that was in $this->params, $this->data,
path information as here, base, webroot

$this->request->params['controller']
$this->request->params('controller')

// Passed arguments
$this->request->pass;
$this->request['pass'];
$this->request->params['pass'];

// URL is /posts/index?page=1&sort=title
$this->request->query['page'];


//--------- data ---------------
// An input with a name attribute equal to 'MyModel[title]' is accessible at
$this->request->data('MyModel.title');

// You can also access the array of data, as an array:
$this->request->data['title'];
$this->request->data['comments'][1]['author'];


//--------- input ---------------
// Applications employing REST often exchange data in non-URL-encoded post bodies. 
// You can read input data in any format using Network\Request::input()
// Get JSON encoded data submitted to a PUT/POST action
$data = $this->request->input('json_decode');

// Get Xml encoded data submitted to a PUT/POST action
$data = $this->request->input('Xml::build', ['return' => 'domdocument']);

//---------- $_ENV and $_SERVER ------------
// Get a value
$value = $this->request->env('HTTP_HOST');

// Set a value. Generally helpful in testing.
$this->request->env('REQUEST_METHOD', 'POST');
Cake3\WhatsNew\Network\Request
  • Checking Request Conditions
  • Access Session Data $this->request->session()
  • Host and Domain Name
  • Working With HTTP Methods & Headers
  • Trusting Proxy Headers
  • Checking Accept Headers
Cake3\WhatsNew\Network\Response

Response provides an interface to wrap the common response-related tasks such as:

  • Sending headers for redirects.
  • Sending content type headers.
  • Sending any header.
  • Sending the response body.

Response

Cake3\WhatsNew\ViewCells

View cells are small mini-controllers that can invoke view logic and render out templates.

 

Cells are ideal for building reusable page components that require interaction with models, view logic, and rendering logic. 

 

To create a cell, define a class in src/View/Cell and a template in src/Template/Cell/

View Cells

namespace App\View\Cell;

use Cake\View\Cell;

class InboxCell extends Cell
{

    public function display()
    {
        $this->loadModel('Messages');
        $unread = $this->Messages->find('unread');
        $this->set('unread_count', $unread->count());
    }

}
Cake3\WhatsNew\ViewCells

Cell templates have an isolated scope:

  • does not share the same View instance as the one used to render template and layout
  • they are unaware of any helper calls made

View Cells

<!-- src/Template/Cell/Inbox/display.ctp -->
<div class="notification-icon">
    You have <?= $unread_count ?> unread messages.
</div>

Loading Cells

// Load an application cell calling display() method
$cell = $this->cell('Inbox');

// Load a plugin cell
$cell = $this->cell('Messaging.Inbox');

// Run the expanded() method on the Inbox cell
$cell = $this->cell('Inbox::expanded');

// passing arguments to a cell Inbox::recent($since)
$cell = $this->cell('Inbox::recent', ['since' => '-3 days']);

// render the cell
<?= $cell ?>

Interactive Console

Cake3\WhatsNew\InteractiveConsole
$ bin/cake console

Welcome to CakePHP v3.0.0 Console
---------------------------------------------------------------
App : App
Path: /Users/mark/projects/cakephp-app/src/
---------------------------------------------------------------
>>> $articles = Cake\ORM\TableRegistry::get('Articles');
// object(Cake\ORM\Table)(
//
// )
>>> $articles->find();

>>> Cake\Routing\Router::parse('/articles/view/1');
// [
//   'controller' => 'Articles',
//   'action' => 'view',
//   'pass' => [
//     0 => '1'
//   ],
//   'plugin' => NULL
// ]

>>> Cake\Routing\Router::url(['controller' => 'Articles', 'action' => 'edit', 99]);
// '/articles/edit/99'

PHP Built in server

Cake3\WhatsNew\PHPBuiltInServer
$ bin/cake server

This will start PHP’s built-in webserver on port 8765.

Open up http://localhost:8765

Questions?

Cake3\Questions

Ask Paolo :)

https://slides.com/albertopagliarini/cakephp-3

CakePHP 3

By Alberto Pagliarini

CakePHP 3

  • 5,775