0

I'm using CakePHP 3.9

I'm trying to set css / script dependency block inside my view cell.

I want it to be fetched inside the head of my layout, but it just don't work. If I want it to work I must set the css dependency inside the view before/after calling the cell.

Is there any way to set the css dependency inside a View Cell?

My view:

<?= $this->cell('Post::post_input'); ?>
....

My view cell:

<?php echo $this->Html->css(['element/post-input'], ['block' => true]);  ?>
<div class="post-input">...</div>

My Layout:

<html>
<head>
...
    <?php
    echo $this->fetch('css');
    echo $this->fetch('script');
    ?>
...
</head>
<body>
    <?php echo $this->fetch('content'); ?>
</body>
</html>

2 Answers2

1

It doesn't work because a cell is a closed environment that uses a separate view instance, so all blocks that you define in its templates, are going to reside in its internal view.

From the docs:

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

Cookbook > Views > View Cells > Implementing the Cell

If you don't want to manually add the CSS block whenever you use the cell, then I would suggest that you build a custom helper that can generate (or even directly echo) the cell and add the block(s), something along the lines of this:

<?php
// in src/View/Helper/PostHelper.php

namespace App\View\Helper;

use Cake\View\Helper;

/**
 * @property \Cake\View\Helper\HtmlHelper $Html
 */
class PostHelper extends Helper
{
    protected $helpers = [
        'Html'
    ];

    protected $wasPostInputCssAdded = false;

    public function postInput()
    {
        if (!$this->wasPostInputCssAdded) {
            $this->wasPostInputCssAdded = true;

            $this->Html->css(['element/post-input'], ['block' => true]);
        }

        return $this->_View->cell('Post::post_input');
    }
}

The following would then add the block and echo the cell:

<?= $this->Post->postInput() ?>

See also Cookbook > Views > Helpers > Creating Helpers

ndm
  • 59,784
  • 9
  • 71
  • 110
1

Another option that doesn't require rewriting templates would be to create a custom Cell class that uses a CellView class. The view class then wraps the cell contents in a layout which can display blocks like you would normally expect in Cake view/layout templating.

<?php
declare(strict_types=1);

namespace App\View;

use Cake\View\Cell as BaseCell;
use Cake\View\View;

class Cell extends BaseCell
{
    /**
     * @inheritDoc
     */
    public function createView(?string $viewClass = null): View
    {
        // use custom CellView to for Cells
        return parent::createView(CellView::class);
    }
}

Then the CellView:

<?php
declare(strict_types=1);

namespace App\View;

class CellView extends AppView
{
    /**
     * @inheritDoc
     */
    public function render(?string $template = null, $layout = null): string
    {
        $cellContents = parent::render($template, $layout);

        // wrap cell contents in "cell" layout
        return $this->renderLayout($cellContents, 'cell');
    }
}

Now that all cells use a layout, all that is left is to create a basic layout used exclusively by cells:

/templates/layout/cell.php (CakePHP 3: /src/Template/Layout/cell.ctp)

<?php
/**
 * @var CellView $this
 */

use App\View\CellView;

echo $this->fetch('styles');
echo $this->fetch('content');
echo $this->fetch('scripts');

After this initial work, all App\View\Cells will use the "cell" layout. I've found this is a bit more intuitive in the long run if you use different blocks or postLinks with blocks.

Note: The code here is for CakePHP 4 but all you should need to do to make it CakePHP 3 compatible is match the method signatures

jeremyharris
  • 7,884
  • 22
  • 31