Skip to content

Commit

Permalink
Element partial templates
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonkelly committed Feb 3, 2024
1 parent 37b19d7 commit 2be8f5a
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/base/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -6001,4 +6001,20 @@ private function _getRelativeElement(mixed $criteria, int $dir): ?ElementInterfa
->id($elementIds[$key + $dir])
->one();
}

/**
* Renders the element using its partial template.
*
* If no partial template exists for the element, its string representation will be output instead.
*
* @return Markup
* @throws InvalidConfigException
* @throws NotSupportedException
* @see ElementHelper::renderElements()
* @since 5.0.0
*/
public function render(): Markup
{
return ElementHelper::renderElements([$this]);
}
}
67 changes: 67 additions & 0 deletions src/config/GeneralConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,36 @@ class GeneralConfig extends BaseConfig
*/
public string $pageTrigger = 'p';

/**
* @var string The path within the `templates` folder where element partial templates will live.
*
* Partial templates are used to render elements when calling [[\craft\elements\db\ElementQuery::render()]],
* [[\craft\elements\ElementCollection::render()]], or [[\craft\base\Element::render()]].
*
* For example, you could render all the entries within a Matrix field like so:
*
* ```twig
* {{ entry.myMatrixField.render() }}
* ```
*
* The full path to a partial template will also include the element type handle (e.g. `asset` or `entry`) and the
* field layout provider’s handle (e.g. the volume handle or entry type handle). For an entry of type `article`,
* that would be: `_partials/entry/article.twig`.
*
* ::: code
* ```php Static Config
* ->partialTemplatesPath('_cp/partials')
* ```
* ```shell Environment Override
* CRAFT_PARTIAL_TEMPLATES_PATH=_cp/partials
* ```
* :::
*
* @group System
* @since 5.0.0
*/
public string $partialTemplatesPath = '_partials';

/**
* @var string|null The query string param that Craft will check when determining the request’s path.
*
Expand Down Expand Up @@ -5255,6 +5285,43 @@ public function pageTrigger(string $value): self
return $this;
}

/**
* The path within the `templates` folder where element partial templates will live.
*
* Partial templates are used to render elements when calling [[\craft\elements\db\ElementQuery::render()]],
* [[\craft\elements\ElementCollection::render()]], or [[\craft\base\Element::render()]].
*
* For example, you could render all the entries within a Matrix field like so:
*
* ```twig
* {{ entry.myMatrixField.render() }}
* ```
*
* The full path to a partial template will also include the element type handle (e.g. `asset` or `entry`) and the
* field layout provider’s handle (e.g. the volume handle or entry type handle). For an entry of type `article`,
* that would be: `_partials/entry/article.twig`.
*
* ::: code
* ```php Static Config
* ->partialTemplatesPath('_cp/partials')
* ```
* ```shell Environment Override
* CRAFT_PARTIAL_TEMPLATES_PATH=_cp/partials
* ```
* :::
*
* @group System
* @param string $value
* @return self
* @see $partialTemplatesPath
* @since 5.0.0
*/
public function partialTemplatesPath(string $value): self
{
$this->partialTemplatesPath = $value;
return $this;
}

/**
* The query string param that Craft will check when determining the request’s path.
*
Expand Down
21 changes: 21 additions & 0 deletions src/elements/ElementCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@

use Craft;
use craft\base\ElementInterface;
use craft\helpers\ElementHelper;
use craft\helpers\Html;
use Illuminate\Support\Collection;
use Twig\Markup;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;

/**
* ElementCollection represents a collection of elements.
Expand Down Expand Up @@ -65,4 +70,20 @@ public function with(array|string $with): static
}
return $this;
}

/**
* Renders the elements using their partial templates.
*
* If no partial template exists for an element, its string representation will be output instead.
*
* @return Markup
* @throws InvalidConfigException
* @throws NotSupportedException
* @see ElementHelper::renderElements()
* @since 5.0.0
*/
public function render(): Markup
{
return ElementHelper::renderElements($this->items);
}
}
18 changes: 18 additions & 0 deletions src/elements/db/ElementQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
use Twig\Markup;
use yii\base\ArrayableTrait;
use yii\base\Exception;
use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
use yii\base\InvalidValueException;
use yii\base\NotSupportedException;
use yii\db\Connection as YiiConnection;
Expand Down Expand Up @@ -1824,6 +1826,22 @@ public function ids(?YiiConnection $db = null): array
return $result;
}

/**
* Executes the query and renders the resulting elements using their partial templates.
*
* If no partial template exists for an element, its string representation will be output instead.
*
* @return Markup
* @throws InvalidConfigException
* @throws NotSupportedException
* @see ElementHelper::renderElements()
* @since 5.0.0
*/
public function render(): Markup
{
return ElementHelper::renderElements($this->all());
}

/**
* Returns the resulting elements set by [[setCachedResult()]], if the criteria params haven’t changed since then.
*
Expand Down
43 changes: 43 additions & 0 deletions src/helpers/ElementHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@
use craft\fieldlayoutelements\CustomField;
use craft\i18n\Locale;
use craft\services\ElementSources;
use craft\web\View;
use DateTime;
use Throwable;
use Twig\Error\LoaderError as TwigLoaderError;
use Twig\Markup;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;

/**
* Class ElementHelper
Expand Down Expand Up @@ -870,4 +875,42 @@ public static function actionConfig(ElementActionInterface $action): array
'settings' => $action->getSettings() ?: null,
];
}

/**
* Renders the given elements using their partial templates.
*
* If no partial template exists for an element, its string representation will be output instead.
*
* @param ElementInterface[] $elements
* @return Markup
* @throws InvalidConfigException
* @throws NotSupportedException
* @since 5.0.0
*/
public static function renderElements(array $elements): Markup
{
$view = Craft::$app->getView();
$generalConfig = Craft::$app->getConfig()->getGeneral();
$output = [];

foreach ($elements as $element) {
$refHandle = $element::refHandle();
if ($refHandle === null) {
throw new NotSupportedException(sprintf('Element type “%s” doesn’t define a reference handle, so it doesn’t support partial templates.', $element::displayName()));
}
$providerHandle = $element->getFieldLayout()?->provider->getHandle();
if ($providerHandle === null) {
throw new InvalidConfigException(sprintf('Element “%s” doesn’t have a field layout provider that defines a handle, so it can’t be rendered with a partial template.', $element));
}
$template = sprintf('%s/%s/%s', $generalConfig->partialTemplatesPath, $refHandle, $providerHandle);
try {
$output[] = $view->renderTemplate($template, [$refHandle => $element], View::TEMPLATE_MODE_SITE);
} catch (TwigLoaderError) {
// fallback to the string representation of the element
$output[] = Html::tag('p', Html::encode((string)$element));
}
}

return new Markup(implode("\n", $output), Craft::$app->charset);
}
}

0 comments on commit 2be8f5a

Please sign in to comment.