Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,16 @@ Craft 6 now uses [Laravel's authorization system](https://laravel.com/docs/12.x/

## Elements

- Added `CraftCms\Cms\Element\ElementCaches` and `CraftCms\Cms\Support\Facades\ElementCaches`.
- Deprecated `craft\services\Elements::getIsCollectingCacheInfo()`. `CraftCms\Cms\Element\ElementCaches::isCollectingCacheInfo()` should be used instead.
- Deprecated `craft\services\Elements::startCollectingCacheInfo()`. `CraftCms\Cms\Element\ElementCaches::startCollectingCacheInfo()` should be used instead.
- Deprecated `craft\services\Elements::collectCacheTags()`. `CraftCms\Cms\Element\ElementCaches::collectCacheTags()` should be used instead.
- Deprecated `craft\services\Elements::setCacheExpiryDate()`. `CraftCms\Cms\Element\ElementCaches::setCacheExpiryDate()` should be used instead.
- Deprecated `craft\services\Elements::collectCacheInfoForElement()`. `CraftCms\Cms\Element\ElementCaches::collectCacheInfoForElement()` should be used instead.
- Deprecated `craft\services\Elements::stopCollectingCacheInfo()`. `CraftCms\Cms\Element\ElementCaches::stopCollectingCacheInfo()` should be used instead.
- Deprecated `craft\services\Elements::invalidateAllCaches()`. `CraftCms\Cms\Element\ElementCaches::invalidateAll()` should be used instead.
- Deprecated `craft\services\Elements::invalidateCachesForElementType()`. `CraftCms\Cms\Element\ElementCaches::invalidateForElementType()` should be used instead.
- Deprecated `craft\services\Elements::invalidateCachesForElement()`. `CraftCms\Cms\Element\ElementCaches::invalidateForElement()` should be used instead.
- Deprecated `craft\errors\InvalidTypeException`. `CraftCms\Cms\Element\Exceptions\InvalidTypeException` should be used instead.
- Deprecated `craft\errors\UnsupportedSiteException`. `CraftCms\Cms\Element\Exceptions\UnsupportedSiteException` should be used instead.

Expand Down
4 changes: 3 additions & 1 deletion src/Address/Addresses.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use CraftCms\Cms\Address\Events\DefineAddressUsedFields;
use CraftCms\Cms\Address\Events\DefineAddressUsedSubdivisionFields;
use CraftCms\Cms\Address\Repositories\SubdivisionRepository;
use CraftCms\Cms\Element\ElementCaches;
use CraftCms\Cms\Field\Fields;
use CraftCms\Cms\FieldLayout\Contracts\FieldLayoutProviderInterface;
use CraftCms\Cms\FieldLayout\FieldLayout;
Expand All @@ -44,6 +45,7 @@ public function __construct(
private SubdivisionRepository $subdivisionRepository,
private AddressFormatRepository $addressFormatRepository,
private Fields $fields,
private ElementCaches $elementCaches,
?FormatterInterface $formatter = null,
) {
$this->formatter = $formatter ?? new DefaultFormatter(
Expand Down Expand Up @@ -283,6 +285,6 @@ public function handleChangedAddressFieldLayout(ConfigEvent $event): void
$this->fields->saveLayout($layout);

// Invalidate user caches
Comment thread
riasvdv marked this conversation as resolved.
Craft::$app->getElements()->invalidateCachesForElementType(Address::class);
$this->elementCaches->invalidateForElementType(Address::class);
}
}
6 changes: 4 additions & 2 deletions src/Asset/Volumes.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use CraftCms\Cms\Asset\Models\Volume as VolumeModel;
use CraftCms\Cms\Asset\Models\VolumeFolder as VolumeFolderModel;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Element\ElementCaches;
use CraftCms\Cms\Field\Enums\TranslationMethod;
use CraftCms\Cms\Field\Fields;
use CraftCms\Cms\FieldLayout\FieldLayout;
Expand Down Expand Up @@ -42,6 +43,7 @@ public function __construct(
private readonly ProjectConfig $projectConfig,
private readonly Assets $assets,
private readonly Folders $folders,
private readonly ElementCaches $elementCaches,
) {}

/** @return Collection<int, int> */
Expand Down Expand Up @@ -236,7 +238,7 @@ public function handleChangedVolume(ConfigEvent $event): void
isNew: $isNewVolume,
));

Craft::$app->getElements()->invalidateCachesForElementType(Asset::class);
$this->elementCaches->invalidateForElementType(Asset::class);
}

/** @param int[] $volumeIds */
Expand Down Expand Up @@ -325,7 +327,7 @@ public function handleDeletedVolume(ConfigEvent $event): void

event(new VolumeDeleted(volume: $volume));

Craft::$app->getElements()->invalidateCachesForElementType(Asset::class);
$this->elementCaches->invalidateForElementType(Asset::class);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Element/Concerns/Structurable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

namespace CraftCms\Cms\Element\Concerns;

use Craft;
use craft\base\ElementInterface;
use CraftCms\Cms\Element\Element;
use CraftCms\Cms\Element\ElementCollection;
use CraftCms\Cms\Element\Events\AfterMoveInStructure;
use CraftCms\Cms\Element\Events\BeforeMoveInStructure;
use CraftCms\Cms\Element\Queries\Contracts\ElementQueryInterface;
use CraftCms\Cms\Element\Queries\ElementQuery;
use CraftCms\Cms\Support\Facades\ElementCaches;
use CraftCms\Cms\Support\Typecast;

/**
Expand Down Expand Up @@ -370,7 +370,7 @@ public function afterMoveInStructure(int $structureId): void
event(new AfterMoveInStructure($this, $structureId));

// Invalidate caches for this element
Craft::$app->getElements()->invalidateCachesForElement($this);
ElementCaches::invalidateForElement($this);
}

private function _getRelativeElement(mixed $criteria, int $direction): ?ElementInterface
Expand Down
163 changes: 163 additions & 0 deletions src/Element/ElementCaches.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Element;

use craft\base\ElementInterface;
use craft\base\NestedElementInterface;
use CraftCms\Cms\Element\Events\InvalidateElementCaches;
use CraftCms\Cms\View\CacheCollectors\DependencyCollector;
use CraftCms\Cms\View\Data\TemplateCacheContext;
use CraftCms\DependencyAwareCache\Dependency\TagDependency;
use DateTime;
use Illuminate\Container\Attributes\Singleton;
use Throwable;
use yii\base\InvalidConfigException;

#[Singleton]
readonly class ElementCaches
{
public function isCollectingCacheInfo(): bool
{
return $this->dependencyCollector()->isCollecting();
}

public function startCollectingCacheInfo(): void
{
$this->dependencyCollector()->begin(new TemplateCacheContext(
cacheKey: '',
global: false,
resources: false,
));
}

/** @param list<string> $tags */
public function collectCacheTags(array $tags): void
{
$this->dependencyCollector()->collectTags($tags);
}

public function setCacheExpiryDate(DateTime $expiryDate): void
{
$this->dependencyCollector()->setExpiryDate($expiryDate);
}

public function collectCacheInfoForElement(ElementInterface $element): void
{
$this->dependencyCollector()->collectElement($element);
}

/**
* @return array{TagDependency|null, int|null}
*/
public function stopCollectingCacheInfo(): array
{
return $this->dependencyCollector()->stop();
Comment thread
riasvdv marked this conversation as resolved.
}

/** @return list<string> */
public function invalidateAll(): array
{
$tags = $this->invalidateTags(['element']);

event(new InvalidateElementCaches($tags));

return $tags;
}

/**
* @param class-string<ElementInterface> $elementType
* @return list<string>
*/
public function invalidateForElementType(string $elementType): array
{
$tags = $this->invalidateTags(["element::$elementType"]);

event(new InvalidateElementCaches($tags));

return $tags;
}

/**
* @return list<string>
*/
public function invalidateForElement(ElementInterface $element): array
{
$tags = $this->invalidateTags($this->tagsForElement($element));

event(new InvalidateElementCaches($tags, $element));

return $tags;
}

/** @return list<string> */
private function invalidateTags(array $tags): array
{
TagDependency::invalidate($tags);

return $tags;
}

/** @return list<string> */
private function tagsForElement(ElementInterface $element): array
{
$elementClass = $element::class;
$tags = [
"element::{$elementClass}::*",
"element::{$element->id}",
];

$rootElement = $element;

if ($element instanceof NestedElementInterface) {
try {
$owner = $element->getOwner();
} catch (InvalidConfigException) {
$owner = null;
}

if ($owner) {
$tags[] = "element::{$owner->id}";

try {
$rootElement = $owner->getRootOwner();
} catch (Throwable) {
$rootElement = $owner;
}
}
}

if ($rootElement->getIsDraft()) {
$tags[] = "element::{$elementClass}::drafts";

return $tags;
}

if ($rootElement->getIsRevision()) {
$tags[] = "element::{$elementClass}::revisions";

return $tags;
}

foreach ($element->getCacheTags() as $tag) {
if (! str_starts_with($tag, 'element::')) {
$tag = "element::{$elementClass}::{$tag}";
}

$tags[] = $tag;
}

return $tags;
}

/**
* We specifically want to resolve the DependencyCollector
* from the container, as it is a scoped service, and
* we don't want it locked inside this singleton.
*/
private function dependencyCollector(): DependencyCollector
{
return app(DependencyCollector::class);
}
}
23 changes: 23 additions & 0 deletions src/Element/Events/InvalidateElementCaches.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Element\Events;

use craft\base\ElementInterface;
use CraftCms\Cms\Element\ElementCaches;

Comment thread
riasvdv marked this conversation as resolved.
class InvalidateElementCaches
{
public function __construct(
/**
* @var string[] An array of TagDependency tag names that are being invalidated
*/
public array $tags,

/**
* @var ElementInterface|null The element whose caches are being invalidated, if this was triggered from {@see ElementCaches::invalidateForElement()}.
*/
public ?ElementInterface $element = null,
) {}
}
6 changes: 4 additions & 2 deletions src/Entry/EntryTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use CraftCms\Cms\Cp\Html\ElementHtml;
use CraftCms\Cms\Cp\Html\PreviewHtml;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Element\ElementCaches;
use CraftCms\Cms\Element\Jobs\ResaveElements;
use CraftCms\Cms\Entry\Data\EntryType;
use CraftCms\Cms\Entry\Elements\Entry;
Expand Down Expand Up @@ -72,6 +73,7 @@ class EntryTypes

public function __construct(
private readonly ProjectConfig $projectConfig,
private readonly ElementCaches $elementCaches,
) {}

/**
Expand Down Expand Up @@ -427,7 +429,7 @@ public function handleChangedEntryType(ConfigEvent $event): void
event(new EntryTypeSaved($entryType, $isNewEntryType));

// Invalidate entry caches
Craft::$app->getElements()->invalidateCachesForElementType(Entry::class);
$this->elementCaches->invalidateForElementType(Entry::class);
}

/**
Expand Down Expand Up @@ -550,7 +552,7 @@ public function handleDeletedEntryType(ConfigEvent $event): void
event(new EntryTypeDeleted($entryType));

// Invalidate entry caches
Craft::$app->getElements()->invalidateCachesForElementType(Entry::class);
$this->elementCaches->invalidateForElementType(Entry::class);
}

/**
Expand Down
9 changes: 7 additions & 2 deletions src/Field/Fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use CraftCms\Cms\Cp\Icons;
use CraftCms\Cms\Database\Expressions\FixedOrderExpression;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Element\ElementCaches;
use CraftCms\Cms\Field\Addresses as AddressesField;
use CraftCms\Cms\Field\Assets as AssetsField;
use CraftCms\Cms\Field\Contracts\ElementContainerFieldInterface;
Expand Down Expand Up @@ -98,6 +99,10 @@ class Fields
*/
private ?array $_allGeneratedFieldHandles = null;

public function __construct(
private readonly ElementCaches $elementCaches,
) {}

// Handle Registry
// -------------------------------------------------------------------------

Expand Down Expand Up @@ -766,7 +771,7 @@ public function applyFieldDelete(string $fieldUid): void
event(new FieldDeleted($field));

// Invalidate all element caches
Craft::$app->getElements()->invalidateAllCaches();
$this->elementCaches->invalidateAll();
}

/**
Expand Down Expand Up @@ -1228,7 +1233,7 @@ public function applyFieldSave(string $fieldUid, array $data, string $context):
}

// Invalidate all element caches
Craft::$app->getElements()->invalidateAllCaches();
$this->elementCaches->invalidateAll();
}

/**
Expand Down
8 changes: 6 additions & 2 deletions src/GarbageCollection/GarbageCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace CraftCms\Cms\GarbageCollection;

use Craft;
use CraftCms\Cms\Address\Elements\Address;
use CraftCms\Cms\Asset\Elements\Asset;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Element\ElementCaches;
use CraftCms\Cms\Entry\Elements\Entry;
use CraftCms\Cms\Field\Elements\ContentBlock;
use CraftCms\Cms\GarbageCollection\Actions\DeleteOrphanedDraftsAndRevisions;
Expand Down Expand Up @@ -70,6 +70,10 @@ class GarbageCollection
*/
public ?OutputInterface $output = null;

public function __construct(
private readonly ElementCaches $elementCaches,
) {}

/**
* Possibly runs garbage collection.
*
Expand Down Expand Up @@ -133,7 +137,7 @@ public function run(bool $force = false): void
]);

// Invalidate all element caches so any hard-deleted elements don't look like they still exist
Craft::$app->getElements()->invalidateAllCaches();
$this->elementCaches->invalidateAll();
}

/**
Expand Down
Loading
Loading