One new feature of CakePHP 3 is the ability to have Model-less form classes. These are typically useful for stuff like contact forms that might send an email:

<?php namespace App\Form; use Cake\Form\Form; use Cake\Form\Schema; use Cake\Mailer\Email; use Cake\Validation\Validator; class ContactForm extends Form { // require some data protected function _buildSchema(Schema $schema) { return $schema->addField('name', 'string') ->addField('email', ['type' => 'string']) ->addField('body', ['type' => 'text']); } // validate the incoming data protected function _buildValidator(Validator $validator) { return $validator->add('name', 'length', [ 'rule' => ['minLength', 10], 'message' => 'A name is required' ])->add('email', 'format', [ 'rule' => 'email', 'message' => 'A valid email address is required', ]); } // actually send an email protected function _execute(array $data) { $email = new Email('default'); return $email->from([$data['email'] => $data['name']]) ->to('mail@example.com', 'Mail Example') ->subject('Contact Form') ->message($data['body']) ->send(); } } ?>

Neato burrito. One thing I love about this is the ability to have complex validation rulesets for specific actions. For instance, on a blog, I might have complex edit action that needs to check for editing writes before allowing a user to do anything:

<?php namespace App\Form; use Cake\Form\Form; class PostEditForm { protected $_user = null; public function __construct(array $user = []) { $this->_user = $user; return $this; } protected function _buildValidator(Validator $validator) { // use $this->_user in my validation rules $userId = $this->_user->get('id'); $validator->add('id', 'custom', [ 'rule' => function ($value, $context) use ($userId) { // reusing an invokable class return (new OwnedByCurrentUser($userId))->__invoke($value); }, 'message' => 'This photo isn\'t yours to battle with' ]); } } ?>

Nifty, huh? Usually I end up saving new records in my _execute() method as well. Here is what that looks like in one of my form classes:

protected function _execute(array $data) { $battle_id = Hash::get($data, 'id', null); $photo_id = Hash::get($data, 'photo_id', null); $battles = TableRegistry::get('Battles'); $photos = TableRegistry::get('Photos'); $battle = $battles->find('Battle', [ 'battle_id' => $battle_id, ])->firstOrFail(); if ($battle->confirmed != null) { throw new MethodNotAllowedException('Battle has already been updated'); } $photo = $photos->get($photo_id); $battle->confirmed = true; $battle->rival->photo = $photo; if ($battles->save($battle)) { return $battles->find('Battle', $battle->toFind())->firstOrFail(); } $exception = new ValidationException('There are errors in the data you submitted'); $exception->errors($battle->errors()); throw $exception; }

Why? Because it turns certain complex actions into the following:

public function edit() { $authedUser = $this->Auth->user(); $post = (new PostEditForm($authedUser))->execute($this->request->data); $this->set(['post' => $post]); }

Instead of litering logic across:

a custom validation method in my model

a controller action

some other random model method or protected controller method

I can group it all together into one, logical unit that can be easily unit tested for various types of input. A side-benefit of this is that if I absolutely need to, I can always re-use a given action’s logic with as few as three lines of code within say, idk, a console shell.