Skip to content

Commit

Permalink
Merge pull request #15894 from craftcms/feature/nested-elemnet-traits
Browse files Browse the repository at this point in the history
Nested element DX improvements
  • Loading branch information
brandonkelly authored Oct 15, 2024
2 parents dcaee97 + fa8bee3 commit eb42e97
Show file tree
Hide file tree
Showing 7 changed files with 602 additions and 996 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
- Added support for passing aliased field handles into element queries’ `select()`/`addSelect()` methods. ([#15827](https://github.com/craftcms/cms/issues/15827))

### Extensibility
- Added `craft\base\NestedElementTrait::saveOwnership()`. ([#15894](https://github.com/craftcms/cms/pull/15894))
- Added `craft\base\RequestTrait::getIsWebRequest()`. ([#15690](https://github.com/craftcms/cms/pull/15690))
- Added `craft\console\Controller::output()`.
- Added `craft\console\controllers\ResaveController::hasTheFields()`.
- Added `craft\elements\db\NestedElementQueryTrait`. ([#15894](https://github.com/craftcms/cms/pull/15894))
- Added `craft\events\ApplyFieldSaveEvent`. ([#15872](https://github.com/craftcms/cms/discussions/15872))
- Added `craft\events\DefineAddressCountriesEvent`. ([#15711](https://github.com/craftcms/cms/pull/15711))
- Added `craft\filters\BasicHttpAuthLogin`. ([#15720](https://github.com/craftcms/cms/pull/15720))
Expand Down
73 changes: 73 additions & 0 deletions src/base/NestedElementTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
namespace craft\base;

use Craft;
use craft\db\Query;
use craft\db\Table;
use craft\elements\db\EagerLoadPlan;
use craft\helpers\Db;
use yii\base\InvalidConfigException;

/**
Expand Down Expand Up @@ -294,4 +297,74 @@ public function setEagerLoadedElements(string $handle, array $elements, EagerLoa
parent::setEagerLoadedElements($handle, $elements, $plan);
}
}

/**
* Saves the element’s ownership data, if it belongs to a field + owner element
*/
private function saveOwnership(bool $isNew, string $elementTable, string $fieldIdColumn = 'fieldId'): void
{
if (!$this->saveOwnership || !isset($this->fieldId)) {
return;
}

$ownerId = $this->getOwnerId();
if (!$ownerId) {
return;
}

if (!isset($this->sortOrder) && (!$isNew || $this->duplicateOf)) {
// figure out if we should proceed this way
// if we're dealing with an element that's being duplicated, and it has a draftId
// it means we're creating a draft of something
// if we're duplicating element via duplicate action - draftId would be empty
$elementId = null;

if ($this->duplicateOf) {
if ($this->draftId) {
$elementId = $this->duplicateOf->id;
}
} else {
// if we're not duplicating, use this element's id
$elementId = $this->id;
}

if ($elementId) {
$this->sortOrder = (new Query())
->select('sortOrder')
->from(Table::ELEMENTS_OWNERS)
->where([
'elementId' => $elementId,
'ownerId' => $ownerId,
])
->scalar() ?: null;
}
}

if (!isset($this->sortOrder)) {
$max = (new Query())
->from(['eo' => Table::ELEMENTS_OWNERS])
->innerJoin(['e' => $elementTable], '[[e.id]] = [[eo.elementId]]')
->where([
'eo.ownerId' => $ownerId,
"e.$fieldIdColumn" => $this->fieldId,
])
->max('[[eo.sortOrder]]');
$this->sortOrder = $max ? $max + 1 : 1;
}

if ($isNew) {
Db::insert(Table::ELEMENTS_OWNERS, [
'elementId' => $this->id,
'ownerId' => $ownerId,
'sortOrder' => $this->sortOrder,
]);
} else {
Db::update(Table::ELEMENTS_OWNERS, [
'sortOrder' => $this->sortOrder,
], [
'elementId' => $this->id,
'ownerId' => $ownerId,
]);
}
}
}
60 changes: 2 additions & 58 deletions src/elements/Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use craft\base\NameTrait;
use craft\base\NestedElementInterface;
use craft\base\NestedElementTrait;
use craft\db\Query;
use craft\db\Table;
use craft\elements\conditions\addresses\AddressCondition;
use craft\elements\conditions\ElementConditionInterface;
Expand All @@ -21,7 +20,6 @@
use craft\fieldlayoutelements\addresses\OrganizationTaxIdField;
use craft\fieldlayoutelements\BaseNativeField;
use craft\fieldlayoutelements\FullNameField;
use craft\helpers\Db;
use craft\models\FieldLayout;
use craft\records\Address as AddressRecord;
use yii\base\InvalidConfigException;
Expand Down Expand Up @@ -722,65 +720,11 @@ public function afterSave(bool $isNew): void

// Capture the dirty attributes from the record
$dirtyAttributes = array_keys($record->getDirtyAttributes());

$record->save(false);

$ownerId = $this->getOwnerId();
if (isset($this->fieldId) && $ownerId && $this->saveOwnership) {
if (!isset($this->sortOrder) && (!$isNew || $this->duplicateOf)) {
// figure out if we should proceed this way
// if we're dealing with an element that's being duplicated, and it has a draftId
// it means we're creating a draft of something
// if we're duplicating element via duplicate action - draftId would be empty
$elementId = null;
if ($this->duplicateOf) {
if ($this->draftId) {
$elementId = $this->duplicateOf->id;
}
} else {
// if we're not duplicating - use element's id
$elementId = $this->id;
}
if ($elementId) {
$this->sortOrder = (new Query())
->select('sortOrder')
->from(Table::ELEMENTS_OWNERS)
->where([
'elementId' => $elementId,
'ownerId' => $ownerId,
])
->scalar() ?: null;
}
}
if (!isset($this->sortOrder)) {
$max = (new Query())
->from(['eo' => Table::ELEMENTS_OWNERS])
->innerJoin(['a' => Table::ADDRESSES], '[[a.id]] = [[eo.elementId]]')
->where([
'eo.ownerId' => $ownerId,
'a.fieldId' => $this->fieldId,
])
->max('[[eo.sortOrder]]');
$this->sortOrder = $max ? $max + 1 : 1;
}
if ($isNew) {
Db::insert(Table::ELEMENTS_OWNERS, [
'elementId' => $this->id,
'ownerId' => $ownerId,
'sortOrder' => $this->sortOrder,
]);
} else {
Db::update(Table::ELEMENTS_OWNERS, [
'sortOrder' => $this->sortOrder,
], [
'elementId' => $this->id,
'ownerId' => $ownerId,
]);
}
}

$this->setDirtyAttributes($dirtyAttributes);

$this->saveOwnership($isNew, Table::ADDRESSES);

parent::afterSave($isNew);
}

Expand Down
61 changes: 3 additions & 58 deletions src/elements/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use craft\controllers\ElementIndexesController;
use craft\db\Connection;
use craft\db\FixedOrderExpression;
use craft\db\Query;
use craft\db\Table;
use craft\elements\actions\Delete;
use craft\elements\actions\DeleteForSite;
Expand Down Expand Up @@ -2518,7 +2517,6 @@ public function afterSave(bool $isNew): void

// Capture the dirty attributes from the record
$dirtyAttributes = array_keys($record->getDirtyAttributes());

$record->save(false);

// save authors
Expand All @@ -2531,60 +2529,9 @@ public function afterSave(bool $isNew): void
}
}

// ownerId will be null when creating a revision
$ownerId = $this->getOwnerId();
if (isset($this->fieldId) && $ownerId && $this->saveOwnership) {
if (!isset($this->sortOrder) && (!$isNew || $this->duplicateOf)) {
// figure out if we should proceed this way
// if we're dealing with an element that's being duplicated, and it has a draftId
// it means we're creating a draft of something
// if we're duplicating element via duplicate action - draftId would be empty
$elementId = null;
if ($this->duplicateOf) {
if ($this->draftId) {
$elementId = $this->duplicateOf->id;
}
} else {
// if we're not duplicating - use element's id
$elementId = $this->id;
}
if ($elementId) {
$this->sortOrder = (new Query())
->select('sortOrder')
->from(Table::ELEMENTS_OWNERS)
->where([
'elementId' => $elementId,
'ownerId' => $ownerId,
])
->scalar() ?: null;
}
}
if (!isset($this->sortOrder)) {
$max = (new Query())
->from(['eo' => Table::ELEMENTS_OWNERS])
->innerJoin(['e' => Table::ENTRIES], '[[e.id]] = [[eo.elementId]]')
->where([
'eo.ownerId' => $ownerId,
'e.fieldId' => $this->fieldId,
])
->max('[[eo.sortOrder]]');
$this->sortOrder = $max ? $max + 1 : 1;
}
if ($isNew) {
Db::insert(Table::ELEMENTS_OWNERS, [
'elementId' => $this->id,
'ownerId' => $ownerId,
'sortOrder' => $this->sortOrder,
]);
} else {
Db::update(Table::ELEMENTS_OWNERS, [
'sortOrder' => $this->sortOrder,
], [
'elementId' => $this->id,
'ownerId' => $ownerId,
]);
}
}
$this->setDirtyAttributes($dirtyAttributes);

$this->saveOwnership($isNew, Table::ENTRIES);

if ($this->getIsCanonical() && isset($this->sectionId) && $section->type == Section::TYPE_STRUCTURE) {
// Has the parent changed?
Expand All @@ -2597,8 +2544,6 @@ public function afterSave(bool $isNew): void
Craft::$app->getElements()->updateDescendantSlugsAndUris($this, true, true);
}
}

$this->setDirtyAttributes($dirtyAttributes);
}

parent::afterSave($isNew);
Expand Down
Loading

0 comments on commit eb42e97

Please sign in to comment.