Skip to content

Commit

Permalink
[6.x] Remove "Parent" field from entries (#11506)
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanmcclean authored Feb 26, 2025
1 parent 2639a2d commit d255f5f
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 114 deletions.
2 changes: 2 additions & 0 deletions resources/js/components/entries/BaseCreateForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
:revisions-enabled="revisions"
:breadcrumbs="breadcrumbs"
:initial-site="site"
:parent="parent"
:can-manage-publish-state="canManagePublishState"
:create-another-url="createAnotherUrl"
:initial-listing-url="listingUrl"
Expand All @@ -41,6 +42,7 @@ export default {
'revisions',
'breadcrumbs',
'site',
'parent',
'canManagePublishState',
'createAnotherUrl',
'listingUrl',
Expand Down
2 changes: 2 additions & 0 deletions resources/js/components/entries/PublishForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ export default {
collectionHasRoutes: Boolean,
previewTargets: Array,
autosaveInterval: Number,
parent: String,
},
data() {
Expand Down Expand Up @@ -620,6 +621,7 @@ export default {
...{
_blueprint: this.fieldset.handle,
_localized: this.localizedFields,
_parent: this.parent,
},
};
Expand Down
1 change: 1 addition & 0 deletions resources/views/entries/create.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
:revisions="{{ Statamic\Support\Str::bool($revisionsEnabled) }}"
:breadcrumbs="{{ $breadcrumbs->toJson() }}"
site="{{ $locale }}"
parent="{{ $parent }}"
create-another-url="{{ cp_route('collections.entries.create', [$collection, $locale, 'blueprint' => $blueprint['handle'], 'parent' => $values['parent'] ?? null]) }}"
listing-url="{{ cp_route('collections.show', $collection) }}"
:can-manage-publish-state="{{ Statamic\Support\Str::bool($canManagePublishState) }}"
Expand Down
10 changes: 0 additions & 10 deletions src/Entries/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,16 +384,6 @@ public function ensureEntryBlueprintFields($blueprint)
$blueprint->ensureField('date', ['type' => 'date', 'required' => true, 'default' => 'now'], 'sidebar');
}

if ($this->hasStructure() && ! $this->orderable()) {
$blueprint->ensureField('parent', [
'type' => 'entries',
'collections' => [$this->handle()],
'max_items' => 1,
'listable' => false,
'localizable' => true,
], 'sidebar');
}

foreach ($this->taxonomies() as $taxonomy) {
if ($blueprint->hasField($taxonomy->handle())) {
continue;
Expand Down
57 changes: 3 additions & 54 deletions src/Http/Controllers/CP/Collections/EntriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,27 +233,7 @@ public function update(Request $request, $collection, $entry)
$tree = $entry->structure()->in($entry->locale());
}

$parent = $values->get('parent');

if ($structure && ! $collection->orderable()) {
$this->validateParent($entry, $tree, $parent);

if (! $entry->revisionsEnabled()) {
$entry->afterSave(function ($entry) use ($parent, $tree) {
if ($parent && optional($tree->find($parent))->isRoot()) {
$parent = null;
}

$tree
->move($entry->id(), $parent)
->save();
});

$entry->remove('parent');
}
}

$this->validateUniqueUri($entry, $tree ?? null, $parent ?? null);
$this->validateUniqueUri($entry, $tree ?? null, $entry->parent()?->id());

if ($entry->revisionsEnabled() && $entry->published()) {
$saved = $entry
Expand Down Expand Up @@ -302,10 +282,6 @@ public function create(Request $request, $collection, $site)

$values = Entry::make()->collection($collection)->values()->all();

if ($collection->hasStructure() && $request->parent) {
$values['parent'] = $request->parent;
}

$fields = $blueprint
->fields()
->addValues($values)
Expand Down Expand Up @@ -349,6 +325,7 @@ public function create(Request $request, $collection, $site)
'canManagePublishState' => User::current()->can('publish '.$collection->handle().' entries'),
'previewTargets' => $collection->previewTargets()->all(),
'autosaveInterval' => $collection->autosaveInterval(),
'parent' => $collection->hasStructure() ? $request->parent : null,
];

if ($request->wantsJson()) {
Expand Down Expand Up @@ -403,7 +380,7 @@ public function store(Request $request, $collection, $site)
}

if ($structure && ! $collection->orderable()) {
$parent = $values['parent'] ?? null;
$parent = $request->_parent;
$entry->afterSave(function ($entry) use ($parent, $tree) {
if ($parent && optional($tree->find($parent))->isRoot()) {
$parent = null;
Expand Down Expand Up @@ -465,34 +442,6 @@ protected function extractAssetsFromValues($values)
->values();
}

private function validateParent($entry, $tree, $parent)
{
if ($entry->id() == $parent) {
throw ValidationException::withMessages(['parent' => __('statamic::validation.parent_cannot_be_itself')]);
}

// If there's no parent selected, the entry will be at end of the top level, which is fine.
// If the entry being edited is not the root, then we don't have anything to worry about.
// If the parent is the root, that's fine, and is handled during the tree update later.
if (! $parent || ! $entry->page()->isRoot()) {
$maxDepth = $entry->collection()->structure()->maxDepth();

// If a parent is selected, validate that it doesn't exceed the max depth of the structure.
if ($parent && $maxDepth && Entry::find($parent)->page()->depth() >= $maxDepth) {
throw ValidationException::withMessages(['parent' => __('statamic::validation.parent_exceeds_max_depth')]);
}

return;
}

// There will always be a next page since we couldn't have got this far with a single page.
$nextTopLevelPage = $tree->pages()->all()->skip(1)->first();

if ($nextTopLevelPage->id() === $parent || $nextTopLevelPage->pages()->all()->count() > 0) {
throw ValidationException::withMessages(['parent' => __('statamic::validation.parent_causes_root_children')]);
}
}

private function validateUniqueUri($entry, $tree, $parent)
{
if (! $uri = $this->entryUri($entry, $tree, $parent)) {
Expand Down
1 change: 1 addition & 0 deletions src/Providers/ExtensionServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ class ExtensionServiceProvider extends ServiceProvider
Updates\AddSitePermissions::class,
Updates\UseClassBasedStatamicUniqueRules::class,
Updates\MigrateSitesConfigToYaml::class,
Updates\RemoveParentField::class,
];

public function register()
Expand Down
18 changes: 0 additions & 18 deletions src/Revisions/Revisable.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,6 @@ public function publishWorkingCopy($options = [])
{
$item = $this->fromWorkingCopy();

if ($item instanceof Entry) {
$parent = $item->get('parent');

$item->remove('parent');
}

$saved = $item
->published(true)
->updateLastModified($user = $options['user'] ?? false)
Expand All @@ -87,18 +81,6 @@ public function publishWorkingCopy($options = [])
return false;
}

if ($item instanceof Entry && $item->collection()->hasStructure() && $parent) {
$tree = $item->collection()->structure()->in($item->locale());

if (optional($tree->find($parent))->isRoot()) {
$parent = null;
}

$tree
->move($this->id(), $parent)
->save();
}

$item
->makeRevision()
->user($user)
Expand Down
30 changes: 30 additions & 0 deletions src/UpdateScripts/RemoveParentField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Statamic\UpdateScripts;

use Statamic\Facades\Collection;

class RemoveParentField extends UpdateScript
{
public function shouldUpdate($newVersion, $oldVersion)
{
return $this->isUpdatingTo('6.0.0');
}

public function update()
{
Collection::all()->each(function ($collection) {
$collection->entryBlueprints()->each(function ($blueprint) use ($collection) {
if ($collection->hasStructure() && $blueprint->hasField('parent')) {
$blueprint->removeField('parent')->save();

$this->console->line(sprintf(
'Parent field removed from the <comment>%s</comment> collection\'s <info>%s</info> blueprint.',
$collection->handle(),
$blueprint->handle()
));
}
});
});
}
}
32 changes: 0 additions & 32 deletions tests/Feature/Entries/UpdateEntryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -437,38 +437,6 @@ public function user_without_permission_to_manage_publish_state_cannot_change_pu
$this->markTestIncomplete();
}

#[Test]
public function validates_max_depth()
{
[$user, $collection] = $this->seedUserAndCollection();

$structure = (new CollectionStructure)->maxDepth(2)->expectsRoot(true);
$collection->structure($structure)->save();

EntryFactory::collection('test')->id('home')->slug('home')->data(['title' => 'Home', 'foo' => 'bar'])->create();
EntryFactory::collection('test')->id('about')->slug('about')->data(['title' => 'About', 'foo' => 'baz'])->create();
EntryFactory::collection('test')->id('team')->slug('team')->data(['title' => 'Team'])->create();

$entry = EntryFactory::collection($collection)
->id('existing-entry')
->slug('existing-entry')
->data(['title' => 'Existing Entry', 'foo' => 'bar'])
->create();

$collection->structure()->in('en')->tree([
['entry' => 'home'],
['entry' => 'about', 'children' => [
['entry' => 'team'],
]],
['entry' => 'existing-entry'],
])->save();

$this
->actingAs($user)
->update($entry, ['title' => 'Existing Entry', 'slug' => 'existing-entry', 'parent' => ['team']]) // This would make it 3 levels deep, so it should fail.
->assertUnprocessable();
}

#[Test]
public function does_not_validate_max_depth_when_collection_max_depth_is_null()
{
Expand Down
82 changes: 82 additions & 0 deletions tests/UpdateScripts/RemoveParentFieldTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Tests\UpdateScripts;

use PHPUnit\Framework\Attributes\Test;
use Statamic\Facades\Blueprint;
use Statamic\Facades\Collection;
use Statamic\UpdateScripts\RemoveParentField;
use Tests\PreventSavingStacheItemsToDisk;
use Tests\TestCase;
use Tests\UpdateScripts\Concerns\RunsUpdateScripts;

class RemoveParentFieldTest extends TestCase
{
use PreventSavingStacheItemsToDisk, RunsUpdateScripts;

#[Test]
public function it_is_registered()
{
$this->assertUpdateScriptRegistered(RemoveParentField::class);
}

#[Test]
public function it_removes_parent_field_from_structured_collection_blueprint()
{
$collection = tap(Collection::make('test')->structureContents(['tree' => []]))->save();

$blueprint = $collection->entryBlueprint();

$blueprint->setContents(['tabs' => [
'main' => ['sections' => [
['fields' => [
['handle' => 'title', 'field' => ['type' => 'text']],
]],
]],
'sidebar' => ['sections' => [
['fields' => [
['handle' => 'slug', 'field' => ['type' => 'slug']],
['handle' => 'parent', 'field' => ['type' => 'entries', 'collections' => ['test'], 'max_items' => 1]],
]],
]],
]]);

$blueprint->save();

$this->runUpdateScript(RemoveParentField::class);

$blueprint = Blueprint::find($blueprint->fullyQualifiedHandle());

$this->assertFalse($blueprint->hasField('parent'));
}

#[Test]
public function it_does_not_remove_parent_field_from_unstructured_collection_blueprint()
{
$collection = tap(Collection::make('test'))->save();

$blueprint = $collection->entryBlueprint();

$blueprint->setContents(['tabs' => [
'main' => ['sections' => [
['fields' => [
['handle' => 'title', 'field' => ['type' => 'text']],
]],
]],
'sidebar' => ['sections' => [
['fields' => [
['handle' => 'slug', 'field' => ['type' => 'slug']],
['handle' => 'parent', 'field' => ['type' => 'entries', 'collections' => ['test'], 'max_items' => 1]],
]],
]],
]]);

$blueprint->save();

$this->runUpdateScript(RemoveParentField::class);

$blueprint = Blueprint::find($blueprint->fullyQualifiedHandle());

$this->assertTrue($blueprint->hasField('parent'));
}
}

0 comments on commit d255f5f

Please sign in to comment.