Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/set default #1362

Merged
merged 14 commits into from
May 12, 2020
31 changes: 31 additions & 0 deletions config/bolt/contenttypes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,37 @@ tests:
values: [ A-tuin, Donatello, Rafael, Leonardo, Michelangelo, Koopa, Squirtle ]
multiple: true
postfix: "Select your favourite turtle(s)."
set_field:
type: set
fields:
title:
type: text
year:
type: number
default:
title: "This is the default title value"
year: 2020
collection_field:
type: collection
fields:
photo:
type: image
paragraph:
type: text
default:
0:
field: photo
default:
filename: "kitten.jpg"
alt: "Cute kitten"
1:
field: paragraph
default: "An image, followed by some content"
2:
field: photo
default:
filename: "joey.jpg"
alt: "Photo of a foal"
taxonomy: [ groups, categories, tags, foobars ]
record_template: custom/test.twig
listing_template: listing.twig
Expand Down
20 changes: 8 additions & 12 deletions src/Controller/Backend/ContentEditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,12 @@ private function removeFieldChildren(Content $content, FieldParentInterface $fie

/** @var Field $child */
$content->removeField($child);
$this->em->remove($child);

// Only attempt removal if the entity is already persisted (managed)
// by the entity manager
if ($this->em->contains($child)) {
$this->em->remove($child);
}
}
}

Expand Down Expand Up @@ -421,19 +426,10 @@ private function updateField(Field $field, $value, ?string $locale): void
$value = Json::findArray($value);
}

if ($field->getType() === SetField::TYPE) {
if ($field instanceof SetField) {
foreach ($value as $name => $svalue) {
/** @var SetField $field */
if ($field->hasChild($name)) {
$child = $field->getChild($name);
} else {
$child = FieldRepository::factory($field->getDefinition()->get('fields')->get($name), $name);
$child->setParent($field);
$field->getContent()->addField($child);
}

$child = $field->getChild($name);
$child->setDefinition($child->getName(), $field->getDefinition()->get('fields')->get($child->getName()));

$this->updateField($child, $svalue, $locale);
}
} else {
Expand Down
6 changes: 4 additions & 2 deletions src/DataFixtures/ContentFixtures.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,14 @@ private function loadSetField(Content $content, Field $set, ContentType $content
{
$setChildren = $set->getDefinition()->get('fields');

$children = [];
foreach ($setChildren as $setChild => $setChildType) {
$child = $this->loadField($content, $setChild, $setChildType, $contentType, $preset, false);
$child->setParent($set);
$content->addField($child);
$children[] = $child;
}

$set->setValue($children);

return $set;
}

Expand Down
5 changes: 2 additions & 3 deletions src/Entity/Field/CollectionField.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,8 @@ public function getDefaultValue()

$result = [];

foreach ($default as $i) {
/** @var ContentType $type */
$type = $default[$i];
/** @var ContentType $type */
foreach ($default as $type) {
$value = $type->toArray()['default'];
$name = $type->toArray()['field'];
$definition = $this->getDefinition()->get('fields')[$name];
Expand Down
17 changes: 17 additions & 0 deletions src/Entity/Field/EmailField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Bolt\Entity\Field;

use Bolt\Entity\Field;
use Bolt\Entity\FieldInterface;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
*/
class EmailField extends Field implements FieldInterface
{
public const TYPE = 'email';
}
76 changes: 62 additions & 14 deletions src/Entity/Field/SetField.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace Bolt\Entity\Field;

use Bolt\Entity\Content;
use Bolt\Entity\Field;
use Bolt\Entity\FieldInterface;
use Bolt\Entity\FieldParentInterface;
use Bolt\Entity\FieldParentTrait;
use Bolt\Repository\FieldRepository;
use Doctrine\ORM\Mapping as ORM;
use Tightenco\Collect\Support\Collection;

/**
* @ORM\Entity
Expand All @@ -22,28 +24,60 @@ class SetField extends Field implements FieldInterface, FieldParentInterface

public function getValue(): array
{
$result = [];
if (empty(parent::getValue())) {
// create new ones from the definition
$fieldDefinitions = $this->getDefinition()->get('fields');

$fieldDefinitions = $this->getDefinition()->get('fields');
if (! is_iterable($fieldDefinitions)) {
return [];
}

// If there's no current $fieldDefinitions, we can return early
if (! is_iterable($fieldDefinitions)) {
return $result;
$newFields = [];
foreach ($fieldDefinitions as $name => $definition) {
$newFields[] = FieldRepository::factory($definition, $name);
}
$this->setValue($newFields);
}

foreach ($fieldDefinitions as $name => $definition) {
if ($this->getContent() && $this->hasChild($name)) {
$field = $this->getChild($name);
$field->setDefinition($name, $definition);
return parent::getValue();
}

public function setValue($fields): Field
{
if (! is_iterable($fields)) {
return $this;
}

$definedFields = array_flip($this->getDefinition()->get('fields', new Collection())->keys()->toArray());

$value = [];

/** @var Field $field */
foreach ($fields as $field) {
$field->setParent($this);
$value[$field->getName()] = $field;
}

// Sorts the fields in the order specified in the definition
$value = array_merge(array_flip(array_intersect(array_keys($definedFields), array_keys($value))), $value);

parent::setValue($value);

return $this;
}

public function setContent(?Content $content): Field
{
/** @var Field $child */
foreach ($this->getValue() as $child) {
if ($content !== null) {
$content->addField($child);
} else {
$field = FieldRepository::factory($definition);
$child->setContent($content);
}

$field->setName($name);
$result[$name] = $field;
}

return $result;
return parent::setContent($content);
}

public function getApiValue()
Expand All @@ -56,4 +90,18 @@ public function getApiValue()

return $result;
}

public function getDefaultValue()
{
$defaultValues = parent::getDefaultValue();
$value = $this->getValue();

foreach ($defaultValues as $name => $default) {
if (array_key_exists($name, $value)) {
$value[$name]->setValue($default);
}
}

return $value;
}
}
14 changes: 6 additions & 8 deletions src/Entity/FieldParentTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ abstract public function getContent(): ?Content;

public function getChild(string $fieldName): Field
{
return $this->getContent()->getRawFields()->filter(function (Field $field) use ($fieldName) {
return $field->getParent() === $this && $field->getName() === $fieldName;
return collect($this->getValue())->filter(function (Field $field) use ($fieldName) {
return $field->getName() === $fieldName;
})->first();
}

public function hasChild(string $fieldName): bool
{
$query = $this->getContent()->getRawFields()->filter(function (Field $field) use ($fieldName) {
return $field->getParent() === $this && $field->getName() === $fieldName;
$query = collect($this->getValue())->filter(function (Field $field) use ($fieldName) {
return $field->getName() === $fieldName;
});

return ! $query->isEmpty();
}

public function hasChildren(): bool
{
$query = $this->getContent()->getRawFields()->filter(function (Field $field) {
$query = collect($this->getValue())->filter(function (Field $field) {
return $field->getParent() === $this;
});

Expand All @@ -38,9 +38,7 @@ public function hasChildren(): bool

public function getChildren(): array
{
return $this->getContent()->getRawFields()->filter(function (Field $field) {
return $field->getParent() === $this;
})->toArray();
return $this->getValue();
}

public function setLocale(?string $locale): Field
Expand Down
6 changes: 5 additions & 1 deletion src/Event/Listener/ContentFillListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ public function preUpdate(LifecycleEventArgs $args): void
// So, let's clear their value.
if ($entity instanceof Content) {
$entity->getFields()->filter(function (Field $field) {
return $field instanceof FieldParentInterface;
// @todo: Allow sets to be cleared as well.
// Now they cannot be cleared, because the value
// can be used in a collection after preUpdate on
// the set is called.
return $field instanceof Field\CollectionField;
})->map(function (Field $field): void {
$field->setValue([]);
});
Expand Down
19 changes: 19 additions & 0 deletions src/Event/Listener/FieldFillListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Bolt\Entity\Field;
use Bolt\Entity\Field\CollectionField;
use Bolt\Entity\Field\SetField;
use Bolt\Repository\FieldRepository;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Tightenco\Collect\Support\Collection;
Expand Down Expand Up @@ -33,6 +34,24 @@ public function postLoad(LifecycleEventArgs $args): void
$this->cfl->fillContent($entity->getContent());
$this->fillCollection($entity);
}

if ($entity instanceof SetField) {
$this->cfl->fillContent($entity->getContent());
$this->fillSet($entity);
}
}

public function fillSet(SetField $entity): void
{
$fields = $this->fields->findAllByParent($entity);

/** @var Field $field */
foreach ($fields as $field) {
$definition = $entity->getDefinition()->get('fields')[$field->getName()] ?? new Collection();
$field->setDefinition($field->getName(), $definition);
}

$entity->setValue($fields);
}

public function fillCollection(CollectionField $entity): void
Expand Down
2 changes: 1 addition & 1 deletion src/Repository/FieldRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function findOneBySlug(string $slug): ?Field

public function findAllByParent(Field $field): ?array
{
if (! $field instanceof FieldParentInterface) {
if (! $field instanceof FieldParentInterface || ! $field->getId()) {
return [];
}

Expand Down
28 changes: 26 additions & 2 deletions tests/e2e/edit_record_1.feature
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,14 @@ Feature: Edit record
And I should see "Collection:" in the "label[for='field-collection']" element

#templates dropdown
When I click "#multiselect-undefined > div > div.multiselect__select"
When I scroll "#multiselect-undefined > div > div.multiselect__select" into view
And I click "#multiselect-undefined > div > div.multiselect__select"

Then I should see "Set" in the "#multiselect-undefined li:nth-child(1) > span" element
And I should see "Textarea" in the "#multiselect-undefined li:nth-child(2) > span" element

When I click "#multiselect-undefined > div > div.multiselect__content-wrapper > ul > li:nth-child(2) > span"
When I click "#multiselect-undefined > div > div.multiselect__select"
And I click "#multiselect-undefined > div > div.multiselect__content-wrapper > ul > li:nth-child(2) > span"
And I press "Add item"

Then I should see an ".collection-item" element
Expand Down Expand Up @@ -318,8 +320,30 @@ Feature: Edit record
And the "fields[title]" field should contain "Title of a test contenttype"
And the "fields[image][filename]" field should contain "foal.jpg"

And the "sets[set_field][title]" field should contain "This is the default title value"
And the "sets[set_field][year]" field should contain "2020"

And the "collections[collection_field][photo][1][filename]" field should contain "kitten.jpg"
And the "collections[collection_field][photo][1][alt]" field should contain "Cute kitten"

And the "collections[collection_field][paragraph][2]" field should contain "An image, followed by some content"

And the "collections[collection_field][photo][3][filename]" field should contain "joey.jpg"
And the "collections[collection_field][photo][3][alt]" field should contain "Photo of a foal"

When I scroll "Save changes" into view
And I press "Save changes"

Then the "fields[title]" field should contain "Title of a test contenttype"
And the "fields[image][filename]" field should contain "foal.jpg"

And the "sets[set_field][title]" field should contain "This is the default title value"
And the "sets[set_field][year]" field should contain "2020"

And the "collections[collection_field][photo][1][filename]" field should contain "kitten.jpg"
And the "collections[collection_field][photo][1][alt]" field should contain "Cute kitten"

And the "collections[collection_field][paragraph][2]" field should contain "An image, followed by some content"

And the "collections[collection_field][photo][3][filename]" field should contain "joey.jpg"
And the "collections[collection_field][photo][3][alt]" field should contain "Photo of a foal"