faster develoment with cakephp 3
DESCRIPTION
TRANSCRIPT
Faster applicationdevelopment with
CakePHP 3.0
1 / 33
Agenda1. A short story2. Grandpas3. Overview of CakePHP 34. Des raisons pour dire ouaou5. Let's get Baking
2 / 33
A Short StoryCakePHP as a project was created in late 2004.
You were probably wearing diapers at the time.
3 / 33
More than a grown upCakePHP is already a grandpa in the frameworks world
4 / 33
Types of Grandpas
The dormant
All cool kids make fun of him.
5 / 33
Types of Grandpas
The bad inluence
All cool kids want to be like him. They do not end well.
6 / 33
Types of Grandpas
The magic one
Has a great deal of experience, helps people in their journeys.
7 / 33
Why CakePHP 3?Very mature. Experience from lots of development years is added into the mix.
Extremely powerful, flexible and lightweight ORM.
Beautifully simple Form generator for single and multiple entities.
Built-in Internationalization, that actually makes sense.
Middleware oriented stack, without sacrificing familiarity and readability.
Solves the 80% problem and stays out of the way.
Outstanding documentation.
Requires less code!
8 / 33
Use it separatelyInteroperability with the rest of the PHP ecosystem
Separate components that you can use in other projects
cakephp/cache
cakephp/event
cakephp/validation
cakephp/collection...
use Cake\Collection\Collection;$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']];
9 / 33
Quick ORM OverviewEverything can be an expression
$query = $users->find()->select(['id'])->where(['is_active' => true]);$anotherQuery->innerJoin(['stuff' => $query]);$anotherQuery->where(['id IN' => $query]);
Queries can be composed
$premium = $users->find('active')->find('premium')->each(function($user) { echo $user->name;});
$subscribers = $users->find('active')->find('subscribedToNewsletter');
Queries have access to powerful collection methods
$recipients = $premium->append($subscribers)->extract('email');
10 / 33
Quick ORM OverviewFiltering by associations
public function findWithCitiesBiggerThanDenmark(Query $query) { $denmarkPopulation = $this->find() ->select(['population']) ->where(['name' => 'Denmark']);
return $query ->matching('Cities', function($q) use ($denmarkPopulation) { return $q->where(['Cities.population >' => $denmarkPopulation]); });}
11 / 33
Quick ORM OverviewLazy results 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": [...
12 / 33
Quick ORM OverviewLazy result processing pipelines
public function findInRegionalGroups(Query $query) { $query ->formatResults(function($results) { return $results->groupBy('continent'); }) ->formatResults(function($results) { return $results->map(function($continent) { return collection($continent)->groupBy('region'); }); }); return $query;}
"North America": { "Caribbean": [ { "name": "Aruba" }, { "name": "Anguilla" }, { "name": "Netherlands Antilles" } ...
13 / 33
MOAR!Map-Reduce
Intelligent count operations
Complex pagination one-liners
Automatic saving of associations
Flexible association strategies
Result streaming
Query caching
Finder callbacks
Composite Primary Key searches
Methods for finding in Tree structures
14 / 33
MOAR!
Find more ORM examples at
https://github.com/lorenzo/cakephp3-examples
15 / 33
Let's Get Baking
16 / 33
Install CakePHPcomposer create-project --prefer-dist -s dev cakephp/app
cd app && bin/cake server
Virtual environment available: https://github.com/FriendsOfCake/vagrant-chef
17 / 33
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('tags') ->addColumn('title', 'string', ['limit' => 100]) ->addColumn('created', 'datetime') ->addColumn('modified', 'datetime') ->create();...
$ bin/cake migrations create Initial$ bin/cake migrations migrate
Reversible migrations provided by http://phinx.org
18 / 33
Put the Cake in the Oven$ bin/cake bake all users$ bin/cake bake all bookmarks$ bin/cake bake all tags
19 / 33
That's it
The End
20 / 33
DebugKitCakePHP 3 comes with a debug toolbar pre-installed:
21 / 33
Next level RADThe code in the controllers looks repetitive, how do we fix this?
How can we have both a Web + REST interface ?
Can we make this without sacrificing readability and flexibility?
Enter the CRUD Plugin
Dynamic Event-Driven Scaffolding
Automatic REST API generation
Actions Classes
Debugging Enhancements
22 / 33
Install the CRUD plugincomposer require friendsofcake/crud:dev-cake3
class AppController extends Controller {
use \Crud\Controller\ControllerTrait;
public function initialize() { $this->loadComponent('Crud.Crud', [ 'actions' => [ 'Crud.Index', 'Crud.Add', 'Crud.Edit', 'Crud.View', 'Crud.Delete' ] ]); }}
Remove all the code from your controllers.
Piece of Cake!
23 / 33
Creating a REST API// config/routes.php
$routes->extensions(['json', 'xml']); // optional
public function initialize() { ... $this->loadComponent('RequestHandler'); $this->Crud->addListener('Crud.Api');}
$ curl -X GET localhost:8765/tags.json
Seriously, that's it
Converts all actions into a web service able to respond in JSON and XML.
Takes care of error handling and validation errors.
Automatically adds a "layout" to your responses (useful for pagination)
Wins you fame and glory beyond your wildest dreams. 24 / 33
Adding Debug info to APIpublic function initialize() { ... $this->Crud->addListener('Crud.Api'); $this->Crud->addListener('Crud.ApiQueryLog');}
25 / 33
Authentication
Hash Passwords
// src/Model/Entity/User.php
use Cake\Auth\DefaultPasswordHasher;...protected function _setPassword($password) { $hasher = new DefaultPasswordHasher(); return $hasher->hash($value);}
Enable Authentication
public function initialize() { $this->loadComponent('Auth');}
Make sure you create a user before this last step.
You will now be prompted for login on all actions. 26 / 33
The Login Action// src/Controller/UsersController.php
public function login() { if ($this->request->is('post')) { $user = $this->Auth->identify(); if ($user) { $this->Auth->setUser($user); return $this->redirect($this->Auth->redirectUrl()); } $this->Flash->error('Your username or password is incorrect.'); }}
And its template
// src/Template/Users/login.ctp<h2>Login</h2><?= $this->Form->create() ?><?= $this->Form->input('username') ?><?= $this->Form->input('password') ?><?= $this->Form->button('Login') ?><?= $this->Form->end() ?>
27 / 33
Permissions
Create the permissions handler
// src/Auth/BookmarksAuthorizeclass BookmarksAuthorize { public function authorize($user, Request $request) { $action = $request->action;
// The add and index actions are always allowed. if (in_array($action, ['index', 'add'])) { return true; }
// All other actions require an id. if (empty($request->params['pass'])) { return false; }
// Check that the bookmark belongs to the current user. $id = $request->params['pass'][0]; $bookmark = $this->registry->getController()->Bookmarks->get($id); if ($bookmark->user_id == $user['id']) { return true; } }}
28 / 33
Permissions
Attach the permissions handler
// src/Controller/BookmarksController.php
public function initialize() { $this->Auth->config('authorize.Bookmarks', [ 'className' => 'App\Auth\BookmarksAuthorize' ]);}
Users will now be able to only see Bookmarks they created themselves.
Yes, that easy.
No, I'm not kidding.
29 / 33
Event Based Crud
Limiting the records by user
// src/Controller/BookmarksController.php$this->Crud->on('beforePaginate', function() { $this->paginate['conditions'] = ['Bookmarks.user_id' => $this->Auth->user('id')];});
Remembering the Bookmark creator
// src/Controller/BookmarksController.php
$this->Crud->on('beforeSave', function($e) { $e->subject->entity->user_id = $this->Auth->user('id');});
30 / 33
Custom Finders
Bookmarks tagged with...
public function findTagged(Query $query, $options) { return $query->matching('Tags', function($q) use ($options) { return $q->where(['Tags.title' => $options['tag']]); });}
Find
$bookmarks->find('tagged', ['tag' => 'awesome']);
31 / 33
Thanks
Find examples at
https://github.com/lorenzo/cakephp3-bookmarkr
https://github.com/lorenzo/cakephp3-examples
32 / 33