View Cells

View cells are small mini-controllers that can invoke view logic and render outtemplates. The idea of cells is borrowed from cells in Ruby, where they fulfill a similar role andpurpose.

When to use Cells

Cells are ideal for building reusable page components that require interactionwith models, view logic, and rendering logic. A simple example would be thecart in an online store, or a data-driven navigation menu in a CMS.

Creating a Cell

To create a cell, define a class in src/View/Cell and a template intemplates/cell/. In this example, we’ll be making a cell to display thenumber of messages in a user’s notification inbox. First, create the class file.Its contents should look like:

  1. namespace App\View\Cell;
  2.  
  3. use Cake\View\Cell;
  4.  
  5. class InboxCell extends Cell
  6. {
  7. public function display()
  8. {
  9. }
  10. }

Save this file into src/View/Cell/InboxCell.php. As you can see, like otherclasses in CakePHP, Cells have a few conventions:

  • Cells live in the App\View\Cell namespace. If you are making a cell ina plugin, the namespace would be PluginName\View\Cell.
  • Class names should end in Cell.
  • Classes should inherit from Cake\View\Cell.

We added an empty display() method to our cell; this is the conventionaldefault method when rendering a cell. We’ll cover how to use other methods laterin the docs. Now, create the file templates/cell/Inbox/display.php. Thiswill be our template for our new cell.

You can generate this stub code quickly using bake:

  1. bin/cake bake cell Inbox

Would generate the code we created above.

Implementing the Cell

Assume that we are working on an application that allows users to send messagesto each other. We have a Messages model, and we want to show the count ofunread messages without having to pollute AppController. This is a perfect usecase for a cell. In the class we just made, add the following:

  1. namespace App\View\Cell;
  2.  
  3. use Cake\View\Cell;
  4.  
  5. class InboxCell extends Cell
  6. {
  7. public function display()
  8. {
  9. $this->loadModel('Messages');
  10. $unread = $this->Messages->find('unread');
  11. $this->set('unread_count', $unread->count());
  12. }
  13. }

Because Cells use the ModelAwareTrait and ViewVarsTrait, they behavevery much like a controller would. We can use the loadModel() and set()methods just like we would in a controller. In our template file, add thefollowing:

  1. <!-- templates/cell/Inbox/display.php -->
  2. <div class="notification-icon">
  3. You have <?= $unread_count ?> unread messages.
  4. </div>

Note

Cell templates have an isolated scope that does not share the same Viewinstance as the one used to render template and layout for the currentcontroller action or other cells. Hence they are unaware of any helper callsmade or blocks set in the action’s template / layout and vice versa.

Loading Cells

Cells can be loaded from views using the cell() method and works the same inboth contexts:

  1. // Load an application cell
  2. $cell = $this->cell('Inbox');
  3.  
  4. // Load a plugin cell
  5. $cell = $this->cell('Messaging.Inbox');

The above will load the named cell class and execute the display() method.You can execute other methods using the following:

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

If you need controller logic to decide which cells to load in a request, you canuse the CellTrait in your controller to enable the cell() method there:

  1. namespace App\Controller;
  2.  
  3. use App\Controller\AppController;
  4. use Cake\View\CellTrait;
  5.  
  6. class DashboardsController extends AppController
  7. {
  8. use CellTrait;
  9.  
  10. // More code.
  11. }

Passing Arguments to a Cell

You will often want to parameterize cell methods to make cells more flexible.By using the second and third arguments of cell(), you can pass actionparameters and additional options to your cell classes, as an indexed array:

  1. $cell = $this->cell('Inbox::recent', ['-3 days']);

The above would match the following function signature:

  1. public function recent($since)
  2. {
  3. }

Rendering a Cell

Once a cell has been loaded and executed, you’ll probably want to render it. Theeasiest way to render a cell is to echo it:

  1. <?= $cell ?>

This will render the template matching the lowercased and underscored version ofour action name, e.g. display.php.

Because cells use View to render templates, you can load additional cellswithin a cell template if required.

Note

Echoing a cell uses the PHP __toString() magic method which prevents PHPfrom showing the filename and line number for any fatal errors raised. Toobtain a meanful error message, it is recommended to use theCell::render() method, for example <?= $cell->render() ?>.

Rendering Alternate Templates

By convention cells render templates that match the action they are executing.If you need to render a different view template, you can specify the templateto use when rendering the cell:

  1. // Calling render() explicitly
  2. echo $this->cell('Inbox::recent', ['-3 days'])->render('messages');
  3.  
  4. // Set template before echoing the cell.
  5. $cell = $this->cell('Inbox');
  6. $cell->viewBuilder()->setTemplate('messages');
  7.  
  8. echo $cell;

Caching Cell Output

When rendering a cell you may want to cache the rendered output if the contentsdon’t change often or to help improve performance of your application. You candefine the cache option when creating a cell to enable & configure caching:

  1. // Cache using the default config and a generated key
  2. $cell = $this->cell('Inbox', [], ['cache' => true]);
  3.  
  4. // Cache to a specific cache config and a generated key
  5. $cell = $this->cell('Inbox', [], ['cache' => ['config' => 'cell_cache']]);
  6.  
  7. // Specify the key and config to use.
  8. $cell = $this->cell('Inbox', [], [
  9. 'cache' => ['config' => 'cell_cache', 'key' => 'inbox_' . $user->id]
  10. ]);

If a key is generated the underscored version of the cell class and templatename will be used.

Note

A new View instance is used to render each cell and these new objectsdo not share context with the main template / layout. Each cell isself-contained and only has access to variables passed as arguments to theView::cell() call.

Paginating Data inside a Cell

Creating a cell that renders a paginated result set can be done by leveragingthe Paginator class of the ORM. An example of paginating a user’s favoritemessages could look like:

  1. namespace App\View\Cell;
  2.  
  3. use Cake\View\Cell;
  4. use Cake\Datasource\Paginator;
  5.  
  6. class FavoritesCell extends Cell
  7. {
  8. public function display($user)
  9. {
  10. $this->loadModel('Messages');
  11.  
  12. // Create a paginator
  13. $paginator = new Paginator();
  14.  
  15. // Paginate the model
  16. $results = $paginator->paginate(
  17. $this->Messages,
  18. $this->request->getQueryParams(),
  19. [
  20. // Use a parameterized custom finder.
  21. 'finder' => ['favorites' => [$user]],
  22.  
  23. // Use scoped query string parameters.
  24. 'scope' => 'favorites',
  25. ]
  26. );
  27.  
  28. $paging = $paginator->getPagingParams() + (array)$this->request->getAttribute('paging');
  29. $this->request = $this->request->withAttribute('paging', $paging));
  30.  
  31. $this->set('favorites', $results);
  32. }
  33. }

The above cell would paginate the Messages model using scopedpagination parameters.

Cell Options

Cells can declare constructor options that are converted into properties whencreating a cell object:

  1. namespace App\View\Cell;
  2.  
  3. use Cake\View\Cell;
  4. use Cake\Datasource\Paginator;
  5.  
  6. class FavoritesCell extends Cell
  7. {
  8. protected $_validCellOptions = ['limit'];
  9.  
  10. protected $limit = 3;
  11.  
  12. public function display($userId)
  13. {
  14. $this->loadModel('Users');
  15. $result = $this->Users->find('friends', ['for' => $userId]);
  16. $this->set('favorites', $result);
  17. }
  18. }

Here we have defined a $limit property and add limit as a cell option.This will allow us to define the option when creating the cell:

  1. $cell = $this->cell('Favorites', [$user->id], ['limit' => 10])

Cell options are handy when you want data available as properties allowing youto override default values.