CakePHP workshop
-
Upload
walther-lalk -
Category
Education
-
view
147 -
download
1
Transcript of CakePHP workshop
![Page 1: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/1.jpg)
CakePHP WorkshopBuild fast, grow solid.
![Page 2: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/2.jpg)
Walther LalkCakePHP core team member
Croogo core team member
Lead Software Developer at
Troop Scouter at 9th Pretoria (Irene) Air Scouts
Husband
![Page 3: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/3.jpg)
Development environmentVirtualBox with VagrantRecommended vagrant box is the FriendsOfCake vagrant box.
Download it by running $ git clone https://github.com/FriendsOfCake/vagrant-chef.git
Then start the vagrant box up with$ cd vagrant-chef$ vagrant up
$ vagrant ssh (For windows you’ll probably need to use Putty)
Add this to your hosts file192.168.13.37 app.dev
Linux or OSX : /etc/hostsWindows: C:\Windows\System32\drivers\etc\hosts
PHP built in serverEnsure you have PHP 5.5.9+ with the Intl and Mbstring and a database server running (with the corresponding PDO extension).
Supported:MySQL,PostgreSQL,SQLite,SQL Server
I will be using this with SQLite.
![Page 4: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/4.jpg)
Following along at home
https://github.com/dakota/phpsa2016-complete
![Page 5: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/5.jpg)
Installing CakePHPRun$ composer create-project cakephp/app app
Then $ cd app
![Page 6: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/6.jpg)
If you are using the vagrant box, then visit http://app.dev
in your browser.Otherwise, wait for the next slide
![Page 7: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/7.jpg)
PHP ServerRun $ bin/cake server
Visit http://localhost:8765/
![Page 8: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/8.jpg)
Getting baked
![Page 9: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/9.jpg)
Database configurationIf you’re using the Vagrant box, and have a green tick for database, then you’re good to go.
PHP Server users probably need to do some config. Recommend simply using SQLite (It’s the easiest to get going).
Open config/app.phpFind the Datasources, replace the default datasource with'default' => [ 'className' => 'Cake\Database\Connection', 'driver' => 'Cake\Database\Driver\Sqlite', 'database' => ROOT . DS . 'database.sqlite', 'encoding' => 'utf8', 'cacheMetadata' => true, 'quoteIdentifiers' => false,],
![Page 10: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/10.jpg)
Bake a database$ bin/cake bake migration CreateMembers first_name:string last_name:string email:string created modified $ bin/cake bake migration CreateEvents title:string description:text start:datetime end:datetime created modified $ bin/cake bake migration CreateEventsMembers event_id:integer:primary member_id:integer:primary
![Page 11: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/11.jpg)
Bake a databaseThe migration bake task can sometimes get confused with composite primary keys, and tries to make each primary key an autoincrement field. We need to fix that!
Open config/Migrations/…_CreateEventsMembers.php and remove the two ‘autoIncrement’ => true, lines.
If you are using SQLite, you need to open all the files in the config/Migrations, and change all instances of ‘default’ => null to be ‘default’ => ‘’
![Page 12: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/12.jpg)
Seed that database$ bin/cake bake seed Members
Open config/Seeds/MembersSeed.php, and update the $data variable
$data = [ [ 'first_name' => 'Walther', 'last_name' => 'Lalk', 'email' => '[email protected]', 'created' => date('Y-m-d H:i:s'), 'modified' => date('Y-m-d H:i:s'), ]];
![Page 13: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/13.jpg)
Seed that database$ bin/cake bake seed Events
Open config/Seeds/EventsSeed.php, and update the $data variable
$data = [ [ 'title' => 'PHP South Africa 2016', 'description' => '', 'start' => '2016-09-28 08:00', 'end' => '2016-09-30 17:00', 'created' => date('Y-m-d H:i:s'), 'modified' => date('Y-m-d H:i:s'), ]];
![Page 14: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/14.jpg)
Running migrations$ bin/cake migrations migrate$ bin/cake migrations seed
![Page 15: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/15.jpg)
Bake your application$ bin/cake bake all Members$ bin/cake bake all Events
Visit /members in your browser, there should be 1 member. Add another one.
Now visit /events in your browser, there should be 1 event. Add another one.
Notice how both of them have a multi-select box for members or events? We’re going to remove that at a later stage and change how it works.
![Page 16: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/16.jpg)
Anatomy of a CakePHP application
![Page 17: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/17.jpg)
Marshalling
![Page 18: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/18.jpg)
Persisting
![Page 19: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/19.jpg)
ValidationOpen src/Model/Table/EventsTable.php, find in the validationDefault method.
Change notEmpty() to be allowEmpty()
$validator ->requirePresence('description', 'create') ->notEmpty('description');
![Page 20: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/20.jpg)
Application rulespublic function buildRules(RulesChecker $rules){ $rules->add( function (Event $event) { return $event->start <= $event->end; }, 'endAfterStart', [ 'errorField' => 'end', 'message' => 'The event cannot end before it has started' ] );
return parent::buildRules($rules);}
![Page 21: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/21.jpg)
The art of knowing who’s there
![Page 22: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/22.jpg)
Adding a password$ bin/cake bake migration AddPasswordToMembers password:string[60]$ bin/cake migrations migrate
Add to src/Model/Entity/MemberEntity.php
protected function _setPassword($password){ if (strlen($password) > 0) { return (new \Cake\Auth\DefaultPasswordHasher())->hash($password); }
return $this->password;}
![Page 23: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/23.jpg)
Adding a passwordAdd to src/Templates/Members/add.ctp and src/Templates/Members/edit.ctpecho $this->Form->input('password', [ 'value' => '']);echo $this->Form->input('password_confirm', [ 'label' => 'Confirm password', ’type' => 'password', 'value' => '']);
![Page 24: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/24.jpg)
Adding a passwordAdd validation to the password, and password confirmation fields.$validator ->requirePresence('password', 'create') ->notEmpty('password', 'You need to provide a password.', 'create') ->minLength('password', 6, 'Your password must be 6 characters or longer');
$condition = function ($context) { return !empty($context['data']['password']);};$validator ->requirePresence('password_confirm', $condition) ->notEmpty('password_confirm', 'Please confirm your password', $condition) ->add('password_confirm', 'mustMatch', [ 'rule' => function ($check, $context) { return $context['data']['password'] === $check; }, 'on' => $condition, 'message' => 'Password does not match' ]);
![Page 25: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/25.jpg)
Adding a passwordEdit your existing members and give them passwords. They will be automatically hashed.
![Page 26: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/26.jpg)
Creating the login actionOpen src/Controller/MembersController.phppublic function login(){ if ($this->request->is('post')) { $member = $this->Auth->identify(); if ($member) { $this->Auth->setUser($member);
return $this->redirect($this->Auth->redirectUrl()); } else { $this->Flash->error(__('Email address or password is incorrect'), [ 'key' => 'auth' ]); } }}
public function logout(){ return $this->redirect($this->Auth->logout());}
![Page 27: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/27.jpg)
Creating the login templateCreate src/Templates/Members/login.ctp
<div class="login form large-12 columns content"> <?php echo $this->Form->create(); echo $this->Form->input('email'); echo $this->Form->input('password'); echo $this->Form->button('Login'); echo $this->Form->end(); ?></div>
![Page 28: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/28.jpg)
Enabling AuthenticationOpen src/Controller/AppController.php, and add to the initialize method.$this->loadComponent('Auth', [ 'loginAction' => [ 'controller' => 'Members', 'action' => 'login', ], 'authenticate' => [ 'Form' => [ 'fields' => ['username' => 'email', 'password' => 'password'], 'userModel' => 'Members' ] ]]);
Try to visit /members now
![Page 29: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/29.jpg)
Event securityWe are adding a organiser to our events. Only the organiser is allowed to change the event.
$ bin/cake bake migration AddOrganiserToEvents organiser_id:integer$ bin/cake migrations migrate
$this->belongsTo('Organiser', [ 'foreignKey' => 'organiser_id', 'className' => 'Members']);
Open src/Model/Table/EventsTable.php, and add to the initialize method.
Open src/Model/Table/MembersTable.php, and add to the initialize method.$this->hasMany('OrganisingEvents', [ 'foreignKey' => 'organiser_id', 'className' => 'Events']);
![Page 30: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/30.jpg)
Event securityEnforce foreign key constraints at an application level by adding the following to the buildRules method in the EventsTable class.$rules->existsIn('organiser_id', 'Organiser', 'That member does not exist in the database.');
![Page 31: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/31.jpg)
Who’s the organiser?The member creating the event should be able to choose who the event organiser is.
In src/Templates/Events/add.ctpecho $this->Form->input('organiser_id', [ 'empty' => '-- Select organiser --', 'options' => $members, 'default' => $this->request->session()->read('Auth.User.id')]);
In src/Templates/Events/edit.ctpecho $this->Form->input('organiser_id', [ 'empty' => '-- Select organiser --', 'options' => $members,]);
![Page 32: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/32.jpg)
Who’s that?Who is organiser 1?
protected $_virtual = [ 'full_name'];
protected function _getFullName(){ return sprintf('%s %s (%s)', $this->first_name, $this->last_name, $this->email);}
Add to the Member entity class
In the MembersTable class, change the displayField to ‘full_name’
Better!
![Page 33: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/33.jpg)
Event securityTo enforce that an event can only be modified by it’s organiser, open src/Controller/EventsController.php and add to the edit method, just after the get() call.if ($event->organiser_id !== $this->Auth->user('id')) { throw new ForbiddenException();}
![Page 34: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/34.jpg)
Member securityTo prevent a member from editing another member’s profile, simply addif ($id !== $this->Auth->user('id')) { throw new ForbiddenException();}
To the edit method in the MembersController.
![Page 35: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/35.jpg)
Allow registrationsAllow new members to register by adding a beforeFilter method to the MembersController.public function beforeFilter(\Cake\Event\Event $event){ $this->Auth->allow('add');
return parent::beforeFilter($event);}
Add make a pretty URL for registration and login. Add to config/routes.php$routes->connect('/register', ['controller' => 'Members', 'action' => 'add']);$routes->connect('/login', ['controller' => 'Members', 'action' => 'login']);
![Page 36: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/36.jpg)
I’m going
![Page 37: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/37.jpg)
Belongs to manyCreate a join table object$ bin/cake bake model EventsMembers
Change'joinTable' => 'events_members'
to'through' => 'EventsMembers','saveStrategy' => \Cake\ORM\Association\BelongsToMany::SAVE_APPEND
in the Members and Events table objects.
![Page 38: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/38.jpg)
Event attendanceAdd a field to capture the type of event attendance
$ bin/cake bake migration AddTypeToEventsMembers type:string[10]$ bin/cake migrations migrate
Add to EventsMember entityconst TYPE_GOING = 'going';const TYPE_INTERESTED = 'interested';const TYPE_NOT_GOING = 'notGoing';
![Page 39: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/39.jpg)
Event attendanceIn EventsTable, addpublic function linkMember(\App\Model\Entity\Event $event, $memberId, $type){ $member = $this->Members->get($memberId); //Add the join data $member->_joinData = new \App\Model\Entity\EventsMember([ 'type' => $type ]);
return $this->association('Members')->link($event, [$member]);}
![Page 40: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/40.jpg)
Event attendanceIn EventsController, addpublic function linkActiveMember($eventId, $type){ $event = $this->Events->get($eventId); if ($this->Events->linkMember($event, $this->Auth->user('id'), $type)) { $this->Flash->success('Registered!'); } else { $this->Flash->error('Something went wrong.'); }
return $this->redirect($this->referer());}
![Page 41: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/41.jpg)
Event attendanceIn the Event entitypublic function memberStatus($memberId, $type){ if (!$this->has('members')) { return false; }
$member = collection($this->members) ->firstMatch(['id' => $memberId]);
if (!$member) { return false; }
return $member->_joinData->type === $type;}
![Page 42: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/42.jpg)
Event attendanceIn src/Template/Events/index.ctp, in the actions table cell<br><?phpif (!$event->memberStatus($this->request->session() ->read('Auth.User.id'), \App\Model\Entity\EventsMember::TYPE_GOING)) { echo $this->Html->link(__('Going'), [ 'action' => 'linkActiveMember', $event->id, \App\Model\Entity\EventsMember::TYPE_GOING ]);}if (!$event->memberStatus($this->request->session() ->read('Auth.User.id'), \App\Model\Entity\EventsMember::TYPE_INTERESTED)) { echo $this->Html->link(__('Interested'), [ 'action' => 'linkActiveMember', $event->id, \App\Model\Entity\EventsMember::TYPE_INTERESTED ]);}if (!$event->memberStatus($this->request->session() ->read('Auth.User.id'), \App\Model\Entity\EventsMember::TYPE_NOT_GOING)) { echo $this->Html->link(__('Not going'), [ 'action' => 'linkActiveMember', $event->id, \App\Model\Entity\EventsMember::TYPE_NOT_GOING ]);}?>
![Page 43: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/43.jpg)
Event attendanceOpen src/Templates/Events/add.ctp and src/Templates/Events/edit.ctp and removeecho $this->Form->input('members._ids', ['options' => $members]);
Open src/Templates/Members/add.ctp and src/Templates/Members/edit.ctp and remove
echo $this->Form->input('events._ids', ['options' => $events]);
![Page 44: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/44.jpg)
We don’t need code where we’re going
![Page 45: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/45.jpg)
Installing CRUD$ composer require friendsofcake/crud$ bin/cake plugin load Crud
use \Crud\Controller\ControllerTrait;
Add the Crud trait to your AppController
And, load the Crud component in the initialize method$this->loadComponent('Crud.Crud', [ 'actions' => [ 'Crud.Index', 'Crud.View', 'Crud.Edit', 'Crud.Add', 'Crud.Delete' ], 'listeners' => [ 'Crud.RelatedModels' ]]);
![Page 46: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/46.jpg)
Remove all your codeOr, at least all index, view, edit, add and delete actions.
![Page 47: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/47.jpg)
It still works!?Thanks to the power of CRUD, everything still works
(Mostly)
![Page 48: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/48.jpg)
Adding back functionalityCreate a AuthListener class in the src/Listener directory<?php
namespace App\Listener;
use Cake\Event\Event;use Cake\Network\Exception\ForbiddenException;use Crud\Listener\BaseListener;
/** * Class AuthListener */class AuthListener extends BaseListener{ /** * Settings * * @var array */ protected $_defaultConfig = [ 'property' => 'id', 'message' => 'You are not allowed to access the requested resource.', 'actions' => ['edit', 'delete'] ];
![Page 49: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/49.jpg)
Adding back functionality public function afterFind(Event $event) { if (!in_array($this->_request()->param('action'), $this->config('actions')) ) { return; }
$entity = $event->subject()->entity; $userId = $this->_controller()->Auth->user('id');
if ($entity->get($this->config('property')) !== $userId) { throw new ForbiddenException($this->config('message')); } }}
![Page 50: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/50.jpg)
Adding back functionalityMembersController beforeFilter, add:$this->Crud->addListener('Auth', 'Auth', [ 'property' => 'id']);
Add a beforeFilter for EventsControllerpublic function beforeFilter(\Cake\Event\Event $event){ $this->Crud->addListener('Auth', 'Auth', [ 'property' => 'organiser_id' ]);
return parent::beforeFilter($event);}
![Page 51: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/51.jpg)
Questions?
![Page 52: CakePHP workshop](https://reader031.fdocuments.net/reader031/viewer/2022020119/58794cb61a28abb1418b5191/html5/thumbnails/52.jpg)
Thank You.