View 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 insrc/Template/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:

    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 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 src/Template/Cell/Inbox/display.ctp. 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.

    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.  
    8. public function display()
    9. {
    10. $this->loadModel('Messages');
    11. $unread = $this->Messages->find('unread');
    12. $this->set('unread_count', $unread->count());
    13. }
    14.  
    15. }

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

    1. <!-- src/Template/Cell/Inbox/display.ctp -->
    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

    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:

    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.  
    2. use App\Controller\AppController;
    3. use Cake\View\CellTrait;
    4.  
    5. class DashboardsController extends AppController
    6. {
    7. use CellTrait;
    8.  
    9. // More code.
    10. }

    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.ctp.

    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 .

    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.

    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. // Paginate the model
    15. $results = $paginator->paginate(
    16. $this->Messages,
    17. $this->request->getQueryParams(),
    18. [
    19. // Use a parameterized custom finder.
    20. 'finder' => ['favorites' => [$user]],
    21.  
    22. // Use scoped query string parameters.
    23. 'scope' => 'favorites',
    24. ]
    25. );
    26.  
    27. $paging = $paginator->getPagingParams() + (array)$request->getParam('paging');
    28. $this->request = $this->request->withParam('paging', $paging));
    29.  
    30. $this->set('favorites', $results);
    31. }
    32. }

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

    New in version 3.5.0: Cake\Datasource\Paginator was added in 3.5.0.

    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])