CakePHP
Build fast, grow solid.
Walther Lalk
CakePHP core team member
Croogo core team member
Lead Software Developer at
Troop Scouter at 9th Pretoria (Irene) Air Scouts
Husband github.com/dakota
@dakotairene
waltherlalk.com
What is CakePHP?
What is CakePHP?
CakePHP is a modern, free, open-source, rapid development framework
for PHP. It's a foundational structure for programmers to create web
applications. Our primary goal is to enable you to work in a structured
and rapid manner–without loss of flexibility. CakePHP takes the
monotony out of web development.
CakePHP?
•One of the original PHP frameworks, dating from late 2004
•An opinionated MVC framework
•Convention over configuration
•Encourages DRY coding principles
•Aims to solve 80% of the problem, and then stay out of your way
CakePHP?
•Active and friendly community
•A core team of highly motivated and experienced developers
•Long support life cycles for major releases
•Backwards compatibility between minor releases
A short history lesson
CakePHP
project started

Early 2005
1.0 released

May 2006
2.0 released

October 2011
3.0 released
March 2015
A new brand for a new era
•PHP world has matured greatly since CakePHP 1.0 and 2.0
•An updated, modern re-imagining of CakePHP was needed with greater
interoperability with the rest of the PHP eco-system
•PHP-FIG (CakePHP was a founding member)
•Composer
•Components
•CakePHP 3.0 was released at CakeFest 2015
•A new, modern brand was announced at CakeFest 2016
CakeFest
•Annual CakePHP conference
•Soon to be twice annually
•2 days of workshops (With a
beginner and advanced track)
•2 days of talks from industry
experts and local developers
CakePHP Community
•Active, supportive and helpful community
•CakePHP Forum – discourse.cakephp.org
•Slack and IRC – cakesf.herokuapp.com
•Stackoverflow
•Large eco-system of plugins and packages
•Regular live training sessions – training.cakephp.org
•An awesome full-time community manager – community@cakephp.org
•@cakephp
CakePHP 3
•A new ORM that is powerful, flexible and lightweight
•Simple Form generator
•Internationalization out the box (And it makes sense)
•Standards complaint (PSR-2, 3, 4 and soon 7)
•Middleware oriented stack
Standalone packages
•You can use bits of CakePHP without using CakePHP
•Separate components that can be used in any PHP project
cakephp/cache

cakephp/event

cakephp/validation

cakephp/orm

cakephp/collection
Collections
use CakeCollectionCollection;



$items = [

['id' => 1, 'name' => 'foo', 'parent' => 'a'],

['id' => 2, 'name' => 'bar', 'parent' => 'b'],

['id' => 3, 'name' => 'baz', 'parent' => 'a'],

];



$combined = (new Collection($items))->combine('id', 'name', 'parent');



// Result will look like this when converted to array

[

'a' => [1 => 'foo', 3 => 'baz'],

'b' => [2 => 'bar']

];
Collections
<?php
$items = ['a' => 1, 'b' => 2, 'c' => 3];
$collection = new Collection($items);
$new = $collection->map(function ($value, $key) {
return $value * 2;
});
// $result contains ['a' => 2, 'b' => 4, 'c' => 6];
$result = $new->toArray();
Powerful ORM and query builder
Looks and feels like SQL (Mostly)
$query->select(['name', 'email'])->where(['status IN' => ['active', 'pending']])->orderDesc('created');
$premium = $users->find('active')->find('premium'));

$subscribers = $users->find('active')->find('subscribedToNewsletter');
$query = $users->find()->select(['id'])->where(['is_active' => true]);

$anotherQuery->innerJoin(['stuff' => $query]);

$anotherQuery->where(['id IN' => $query]);
$recipients = $premium->append($subscribers)->extract('email');
Queries can be composed
Everything can be an expression
Queries are collections
Filtering by associations
// UsersTable

public function findWithArticlesNewerThanUser(Query $query, $options)

{

$userDate = $this->find()

->select(['created'])

->where(['id' => $options['userId']]);



return $query

->matching('Articles', function ($q) use ($userDate) {

return $q->where(['created >=' => $userDate]); 

});

}
Post-processing
public function findInContinentGroups(Query $query)

{

$query->formatResults(function ($results) {

return $results->groupBy('continent');

});



return $query;

}
"Africa": [

{

"name": "Angola"

},

{

"name": "Burundi"

},

{

"name": "Benin"

},

{

"name": "Burkina Faso"

},

],

"America": [...
Map-reduce
$mapper = function ($article, $key, $mapReduce) {

if (stripos('cakephp', $article['body']) === false) {

return;

}



$words = array_map('strtolower', explode(' ', $article['body']));

foreach ($words as $word) {

$mapReduce->emitIntermediate($article['id'], $word);

}

};



$reducer = function ($occurrences, $word, $mapReduce) {

$mapReduce->emit(count($occurrences), $word);

}



$articlesByStatus = $articles->find()->mapReduce($mapper, $reducer);
Map-reduce
// Result

[

'cakephp' => 100,

'awesome' => 39,

'impressive' => 57,

'outstanding' => 10,

'mind-blowing' => 83

]
PSR-7 support
•3.3 introduced a PSR-7 compatible middleware stack
•Both request and response objects are immutable
•Application level request/response objects are not PSR-7 (Yet!)
Middleware
CORS
Exceptions
Assets
Routes
App
Middleware
CORS
Exceptions
Assets
Routes
App
Request
Response
Middleware
CORS
Exceptions
Assets
Routes
AppRequest
Response
Rules to follow
•Must accept a Request, Response and ‘next’
•Must return a Response or call ‘next’
Doesn’t have to be limited to the CakePHP eco-system

For example, https://github.com/oscarotero/psr7-middlewares has a collection of
generic middleware classes.
Middleware setup
$middleware = new CakeHttpMiddlewareQueue();



$middleware

// Catch any exceptions in the lower layers,

// and make an error page/response

->add(new ErrorHandlerMiddleware())



// Handle plugin/theme assets like CakePHP normally does.

->add(new AssetMiddleware())



// Apply routing

->add(new RoutingMiddleware());



// Apply CORS as the first middleware

$middleware->prepend(new CorsMiddleware());
Middleware setup
// Oh wow, closures

$ohWow = function ($req, $res, $next) {

$res = $res->withHeader('X-WOW', 'Oh wow!');



return $next($req, $res);

};


// Add to the middleware stack

$middleware->add($ohWow);
Middleware setup
class OhWowMiddleware

{

public function __invoke($req, $res, $next)

{

$res = $res->withHeader('X-WOW', 'Oh wow!');



return $next($req, $res);

}

}



// Add to the middleware stack

$middleware->add(new OhWowMiddleware());
Easy, one-liner pagination (Even for complex queries)
Automatic association saving
Flexible association strategies
Result streaming
Query caching
Finder callbacks
Composite key support (Including foreign keys)
Tree structures
CSRF protection
Form tampering protection
Mailer classes
Authentication
Code generation
Advanced date and time support (Thanks to the Chronos library)
The future
The future*
•3.4 – Early 2017
•PSR-7 request objects
•Conditional Middleware’s (aka Pipelines)
•3.5 – Probably middle 2017
•PSR-7 response objects
•Migrating certain bits of functionality into middleware
•3.6 – Probably end 2017
•Hard deprecate the soft deprecated functionality
•4.0 – Maybe early 2018
•Clean-up by removing all deprecated functionality
https://github.com/cakephp/cakephp/wiki
* Subject to change depending on community feedback
Let’s get baking
Install CakePHP
Full virtual environment available https://
github.com/FriendsOfCake/vagrant-chef
$ composer create-project cakephp/app
$ cd app && bin/cake server
Easier than baking a cake!
Initial database migration
$ bin/cake migrations create Initial
$ bin/cake migrations migrate
Create an empty migration file
$ bin/cake bake migration_snapshot Initial
Create a migration based on an existing database
$ bin/cake bake migration CreateBookmarks user_id:integer title:string[50]
description:text url:text created modified
Build a migration in the console
Run migrations
Initial database migration
$this->table('bookmarks')

->addColumn('user_id', 'integer')

->addColumn('title', 'string', ['limit' => 50])

->addColumn('description', 'text')

->addColumn('url', 'text')

->addColumn('created', 'datetime')

->addColumn('modified', 'datetime')

->create();



$this->table('bookmarks_tags', ['id' => false, 'primary_key' =>
['bookmark_id', 'tag_id']])

->addColumn('bookmark_id', 'integer')
…
Skipping the hard work
$ bin/cake bake all users
$ bin/cake bake all bookmarks
$ bin/cake bake all tags
$ bin/cake bake all --everything
Fixing broken stuff
•Interactive stack trace
•Method argument inspection
•Possible solution hints
DebugKit
•CakePHP 3 comes with a debug
toolbar pre-installed
•Facilitates in-depth application
inspection
Putting the rapid into RAD
•How do we prevent repetitive controller code?
•How do we have both a web and REST interface?
•How do we do this without creating messy spaghetti code?
The CRUD plugin
•Dynamic, event-driven and production ready scaffolding
•Automatic REST API generation
•Single responsibility action classes
Installing CRUD
•Remove all code from your controllers
•Go on holiday, your work is done
class AppController extends Controller

{

use CrudControllerControllerTrait;



public function initialize()

{

$this->loadComponent('Crud.Crud', [

'actions' => [

'Crud.Index',

'Crud.Add',

'Crud.Edit',

'Crud.View',

'Crud.Delete'

]

]);

}

}
$ composer require friendsofcake/crud
Creating a REST API
•Converts all actions into a web service capable of responding in JSON or XML
•Takes care of all error handling and validation errors
•Automatically adds a “layout” to your responses
// config/routes.php

$routes->extensions(['json', 'xml']);
public function initialize()

{

...

$this->loadComponent('RequestHandler');



$this->Crud->addListener('Crud.Api');

$this->Crud->addListener('Crud.ApiPagination');

$this->Crud->addListener('Crud.ApiQueryLog');

}
$ curl –X GET localhost:8765/tags.json
Extending Crud with events
Limiting records by user
Remembering the bookmark creator
$this->Crud->on('beforePaginate', function (CakeEventEvent $event) {

$event->subject()->query->where(['Bookmarks.user_id' => $this->Auth->user('id')]);

});
$this->Crud->on('beforeSave', function (CakeEventEvent $event) {

$event->subject()->entity->user_id = $this->Auth->user('id');

});
Other awesome plugins
•TinyAuth – Light weight role based authentication
•Friends of Cake Search – Simple interface for creating paginate-able filters
•FractalTransformerView – Use Fractal to render API responses
•TwigView – Use Twig for your templates
•Liquid – Use Liquid for your templates
•CakePDF – Easily create PDF files
•The Queue plugin – Dependency-free worker queues
Many many more available from https://github.com/friendsofcake/awesome-cakephp
Also keep an eye on the @cakephp twitter feed for weekly featured plugins!
Thank You.
github.com/dakota
@dakotairene
waltherlalk.com

CakePHP

  • 1.
  • 2.
    Walther Lalk CakePHP coreteam member Croogo core team member Lead Software Developer at Troop Scouter at 9th Pretoria (Irene) Air Scouts Husband github.com/dakota @dakotairene waltherlalk.com
  • 3.
  • 4.
    What is CakePHP? CakePHP isa modern, free, open-source, rapid development framework for PHP. It's a foundational structure for programmers to create web applications. Our primary goal is to enable you to work in a structured and rapid manner–without loss of flexibility. CakePHP takes the monotony out of web development.
  • 5.
    CakePHP? •One of theoriginal PHP frameworks, dating from late 2004 •An opinionated MVC framework •Convention over configuration •Encourages DRY coding principles •Aims to solve 80% of the problem, and then stay out of your way
  • 6.
    CakePHP? •Active and friendlycommunity •A core team of highly motivated and experienced developers •Long support life cycles for major releases •Backwards compatibility between minor releases
  • 7.
    A short historylesson CakePHP project started
 Early 2005 1.0 released
 May 2006 2.0 released
 October 2011 3.0 released March 2015
  • 8.
    A new brandfor a new era •PHP world has matured greatly since CakePHP 1.0 and 2.0 •An updated, modern re-imagining of CakePHP was needed with greater interoperability with the rest of the PHP eco-system •PHP-FIG (CakePHP was a founding member) •Composer •Components •CakePHP 3.0 was released at CakeFest 2015 •A new, modern brand was announced at CakeFest 2016
  • 10.
    CakeFest •Annual CakePHP conference •Soonto be twice annually •2 days of workshops (With a beginner and advanced track) •2 days of talks from industry experts and local developers
  • 11.
    CakePHP Community •Active, supportiveand helpful community •CakePHP Forum – discourse.cakephp.org •Slack and IRC – cakesf.herokuapp.com •Stackoverflow •Large eco-system of plugins and packages •Regular live training sessions – training.cakephp.org •An awesome full-time community manager – community@cakephp.org •@cakephp
  • 12.
    CakePHP 3 •A newORM that is powerful, flexible and lightweight •Simple Form generator •Internationalization out the box (And it makes sense) •Standards complaint (PSR-2, 3, 4 and soon 7) •Middleware oriented stack
  • 13.
    Standalone packages •You canuse bits of CakePHP without using CakePHP •Separate components that can be used in any PHP project cakephp/cache
 cakephp/event
 cakephp/validation
 cakephp/orm
 cakephp/collection
  • 14.
    Collections use CakeCollectionCollection;
 
 $items =[
 ['id' => 1, 'name' => 'foo', 'parent' => 'a'],
 ['id' => 2, 'name' => 'bar', 'parent' => 'b'],
 ['id' => 3, 'name' => 'baz', 'parent' => 'a'],
 ];
 
 $combined = (new Collection($items))->combine('id', 'name', 'parent');
 
 // Result will look like this when converted to array
 [
 'a' => [1 => 'foo', 3 => 'baz'],
 'b' => [2 => 'bar']
 ];
  • 15.
    Collections <?php $items = ['a'=> 1, 'b' => 2, 'c' => 3]; $collection = new Collection($items); $new = $collection->map(function ($value, $key) { return $value * 2; }); // $result contains ['a' => 2, 'b' => 4, 'c' => 6]; $result = $new->toArray();
  • 16.
    Powerful ORM andquery builder Looks and feels like SQL (Mostly) $query->select(['name', 'email'])->where(['status IN' => ['active', 'pending']])->orderDesc('created'); $premium = $users->find('active')->find('premium'));
 $subscribers = $users->find('active')->find('subscribedToNewsletter'); $query = $users->find()->select(['id'])->where(['is_active' => true]);
 $anotherQuery->innerJoin(['stuff' => $query]);
 $anotherQuery->where(['id IN' => $query]); $recipients = $premium->append($subscribers)->extract('email'); Queries can be composed Everything can be an expression Queries are collections
  • 17.
    Filtering by associations //UsersTable
 public function findWithArticlesNewerThanUser(Query $query, $options)
 {
 $userDate = $this->find()
 ->select(['created'])
 ->where(['id' => $options['userId']]);
 
 return $query
 ->matching('Articles', function ($q) use ($userDate) {
 return $q->where(['created >=' => $userDate]); 
 });
 }
  • 18.
    Post-processing public function findInContinentGroups(Query$query)
 {
 $query->formatResults(function ($results) {
 return $results->groupBy('continent');
 });
 
 return $query;
 } "Africa": [
 {
 "name": "Angola"
 },
 {
 "name": "Burundi"
 },
 {
 "name": "Benin"
 },
 {
 "name": "Burkina Faso"
 },
 ],
 "America": [...
  • 19.
    Map-reduce $mapper = function($article, $key, $mapReduce) {
 if (stripos('cakephp', $article['body']) === false) {
 return;
 }
 
 $words = array_map('strtolower', explode(' ', $article['body']));
 foreach ($words as $word) {
 $mapReduce->emitIntermediate($article['id'], $word);
 }
 };
 
 $reducer = function ($occurrences, $word, $mapReduce) {
 $mapReduce->emit(count($occurrences), $word);
 }
 
 $articlesByStatus = $articles->find()->mapReduce($mapper, $reducer);
  • 20.
    Map-reduce // Result
 [
 'cakephp' =>100,
 'awesome' => 39,
 'impressive' => 57,
 'outstanding' => 10,
 'mind-blowing' => 83
 ]
  • 21.
    PSR-7 support •3.3 introduceda PSR-7 compatible middleware stack •Both request and response objects are immutable •Application level request/response objects are not PSR-7 (Yet!)
  • 22.
  • 23.
  • 24.
  • 25.
    Rules to follow •Mustaccept a Request, Response and ‘next’ •Must return a Response or call ‘next’ Doesn’t have to be limited to the CakePHP eco-system
 For example, https://github.com/oscarotero/psr7-middlewares has a collection of generic middleware classes.
  • 26.
    Middleware setup $middleware =new CakeHttpMiddlewareQueue();
 
 $middleware
 // Catch any exceptions in the lower layers,
 // and make an error page/response
 ->add(new ErrorHandlerMiddleware())
 
 // Handle plugin/theme assets like CakePHP normally does.
 ->add(new AssetMiddleware())
 
 // Apply routing
 ->add(new RoutingMiddleware());
 
 // Apply CORS as the first middleware
 $middleware->prepend(new CorsMiddleware());
  • 27.
    Middleware setup // Ohwow, closures
 $ohWow = function ($req, $res, $next) {
 $res = $res->withHeader('X-WOW', 'Oh wow!');
 
 return $next($req, $res);
 }; 
 // Add to the middleware stack
 $middleware->add($ohWow);
  • 28.
    Middleware setup class OhWowMiddleware
 {
 publicfunction __invoke($req, $res, $next)
 {
 $res = $res->withHeader('X-WOW', 'Oh wow!');
 
 return $next($req, $res);
 }
 }
 
 // Add to the middleware stack
 $middleware->add(new OhWowMiddleware());
  • 29.
    Easy, one-liner pagination(Even for complex queries) Automatic association saving Flexible association strategies Result streaming Query caching Finder callbacks Composite key support (Including foreign keys) Tree structures CSRF protection Form tampering protection Mailer classes Authentication Code generation Advanced date and time support (Thanks to the Chronos library)
  • 30.
  • 31.
    The future* •3.4 –Early 2017 •PSR-7 request objects •Conditional Middleware’s (aka Pipelines) •3.5 – Probably middle 2017 •PSR-7 response objects •Migrating certain bits of functionality into middleware •3.6 – Probably end 2017 •Hard deprecate the soft deprecated functionality •4.0 – Maybe early 2018 •Clean-up by removing all deprecated functionality https://github.com/cakephp/cakephp/wiki * Subject to change depending on community feedback
  • 32.
  • 33.
    Install CakePHP Full virtualenvironment available https:// github.com/FriendsOfCake/vagrant-chef $ composer create-project cakephp/app $ cd app && bin/cake server Easier than baking a cake!
  • 34.
    Initial database migration $bin/cake migrations create Initial $ bin/cake migrations migrate Create an empty migration file $ bin/cake bake migration_snapshot Initial Create a migration based on an existing database $ bin/cake bake migration CreateBookmarks user_id:integer title:string[50] description:text url:text created modified Build a migration in the console Run migrations
  • 35.
    Initial database migration $this->table('bookmarks')
 ->addColumn('user_id','integer')
 ->addColumn('title', 'string', ['limit' => 50])
 ->addColumn('description', 'text')
 ->addColumn('url', 'text')
 ->addColumn('created', 'datetime')
 ->addColumn('modified', 'datetime')
 ->create();
 
 $this->table('bookmarks_tags', ['id' => false, 'primary_key' => ['bookmark_id', 'tag_id']])
 ->addColumn('bookmark_id', 'integer') …
  • 36.
    Skipping the hardwork $ bin/cake bake all users $ bin/cake bake all bookmarks $ bin/cake bake all tags $ bin/cake bake all --everything
  • 37.
    Fixing broken stuff •Interactivestack trace •Method argument inspection •Possible solution hints
  • 38.
    DebugKit •CakePHP 3 comeswith a debug toolbar pre-installed •Facilitates in-depth application inspection
  • 39.
    Putting the rapidinto RAD •How do we prevent repetitive controller code? •How do we have both a web and REST interface? •How do we do this without creating messy spaghetti code? The CRUD plugin •Dynamic, event-driven and production ready scaffolding •Automatic REST API generation •Single responsibility action classes
  • 40.
    Installing CRUD •Remove allcode from your controllers •Go on holiday, your work is done class AppController extends Controller
 {
 use CrudControllerControllerTrait;
 
 public function initialize()
 {
 $this->loadComponent('Crud.Crud', [
 'actions' => [
 'Crud.Index',
 'Crud.Add',
 'Crud.Edit',
 'Crud.View',
 'Crud.Delete'
 ]
 ]);
 }
 } $ composer require friendsofcake/crud
  • 41.
    Creating a RESTAPI •Converts all actions into a web service capable of responding in JSON or XML •Takes care of all error handling and validation errors •Automatically adds a “layout” to your responses // config/routes.php
 $routes->extensions(['json', 'xml']); public function initialize()
 {
 ...
 $this->loadComponent('RequestHandler');
 
 $this->Crud->addListener('Crud.Api');
 $this->Crud->addListener('Crud.ApiPagination');
 $this->Crud->addListener('Crud.ApiQueryLog');
 } $ curl –X GET localhost:8765/tags.json
  • 42.
    Extending Crud withevents Limiting records by user Remembering the bookmark creator $this->Crud->on('beforePaginate', function (CakeEventEvent $event) {
 $event->subject()->query->where(['Bookmarks.user_id' => $this->Auth->user('id')]);
 }); $this->Crud->on('beforeSave', function (CakeEventEvent $event) {
 $event->subject()->entity->user_id = $this->Auth->user('id');
 });
  • 43.
    Other awesome plugins •TinyAuth– Light weight role based authentication •Friends of Cake Search – Simple interface for creating paginate-able filters •FractalTransformerView – Use Fractal to render API responses •TwigView – Use Twig for your templates •Liquid – Use Liquid for your templates •CakePDF – Easily create PDF files •The Queue plugin – Dependency-free worker queues Many many more available from https://github.com/friendsofcake/awesome-cakephp Also keep an eye on the @cakephp twitter feed for weekly featured plugins!
  • 44.