diff --git a/migrations/Version20240603210209.php b/migrations/Version20240603210209.php new file mode 100644 index 00000000..7aa4ccd3 --- /dev/null +++ b/migrations/Version20240603210209.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE attributes ADD weight INT DEFAULT 0 NOT NULL'); + $this->addSql('CREATE INDEX IDX_319B9E707CD5541 ON attributes (weight)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX IDX_319B9E707CD5541 ON attributes'); + $this->addSql('ALTER TABLE attributes DROP weight'); + } +} diff --git a/src/Entity/Attribute.php b/src/Entity/Attribute.php index 1b97e397..2669d193 100644 --- a/src/Entity/Attribute.php +++ b/src/Entity/Attribute.php @@ -4,17 +4,21 @@ namespace RZ\Roadiz\CoreBundle\Entity; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Metadata\ApiFilter; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation as Serializer; +use RZ\Roadiz\Core\AbstractEntities\AbstractEntity; +use RZ\Roadiz\CoreBundle\Model\AttributeInterface; +use RZ\Roadiz\CoreBundle\Model\AttributeTrait; use RZ\Roadiz\CoreBundle\Model\RealmInterface; use RZ\Roadiz\CoreBundle\Repository\AttributeRepository; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation as SymfonySerializer; -use RZ\Roadiz\CoreBundle\Model\AttributeInterface; -use RZ\Roadiz\CoreBundle\Model\AttributeTrait; -use RZ\Roadiz\Core\AbstractEntities\AbstractEntity; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Range; /** * @package RZ\Roadiz\CoreBundle\Entity @@ -25,6 +29,7 @@ ORM\Index(columns: ["code"]), ORM\Index(columns: ["type"]), ORM\Index(columns: ["searchable"]), + ORM\Index(columns: ["weight"]), ORM\Index(columns: ["group_id"]), ORM\HasLifecycleCallbacks, UniqueEntity(fields: ["code"]), @@ -62,6 +67,20 @@ class Attribute extends AbstractEntity implements AttributeInterface #[Serializer\Exclude] private ?RealmInterface $defaultRealm = null; + /** + * @var int Absolute weight for sorting attributes in filtered lists. + */ + #[ + ORM\Column(type: "integer", nullable: false, options: ["default" => 0]), + Serializer\Type("integer"), + Serializer\Groups(["attribute", "node", "nodes_sources"]), + SymfonySerializer\Groups(["attribute", "node", "nodes_sources"]), + ApiFilter(OrderFilter::class), + Range(min: 0, max: 9999), + NotNull, + ] + protected int $weight = 0; + public function __construct() { $this->attributeTranslations = new ArrayCollection(); @@ -100,6 +119,17 @@ public function setDefaultRealm(?RealmInterface $defaultRealm): Attribute return $this; } + public function getWeight(): int + { + return $this->weight; + } + + public function setWeight(?int $weight): Attribute + { + $this->weight = $weight ?? 0; + return $this; + } + /** * @return Collection */ diff --git a/src/Form/AttributeType.php b/src/Form/AttributeType.php index 4e755d3b..cd461657 100644 --- a/src/Form/AttributeType.php +++ b/src/Form/AttributeType.php @@ -10,6 +10,7 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; +use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -59,6 +60,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'required' => false, 'help' => 'attributes.form_help.searchable' ]) + ->add('weight', NumberType::class, [ + 'label' => 'attributes.weight', + 'required' => false, + 'scale' => 1, + 'help' => 'attributes.form_help.weight' + ]) ->add('defaultRealm', RealmChoiceType::class, [ 'label' => 'attributes.defaultRealm', 'help' => 'attributes.defaultRealm.help', diff --git a/src/Model/AttributeInterface.php b/src/Model/AttributeInterface.php index 615500c4..1da81a62 100644 --- a/src/Model/AttributeInterface.php +++ b/src/Model/AttributeInterface.php @@ -129,6 +129,8 @@ public function getType(): int; */ public function getColor(): ?string; + public function getWeight(): int; + /** * @param string|null $color */ diff --git a/src/Model/AttributeValueTrait.php b/src/Model/AttributeValueTrait.php index 887475cd..3a50fd72 100644 --- a/src/Model/AttributeValueTrait.php +++ b/src/Model/AttributeValueTrait.php @@ -28,6 +28,9 @@ trait AttributeValueTrait ApiFilter(BaseFilter\BooleanFilter::class, properties: [ "attribute.visible", "attribute.searchable" + ]), + ApiFilter(BaseFilter\OrderFilter::class, properties: [ + "attribute.weight" => "DESC", ]) ] protected AttributeInterface $attribute; diff --git a/src/Serializer/Normalizer/AttributeValueNormalizer.php b/src/Serializer/Normalizer/AttributeValueNormalizer.php index 4267e776..b125a1a3 100644 --- a/src/Serializer/Normalizer/AttributeValueNormalizer.php +++ b/src/Serializer/Normalizer/AttributeValueNormalizer.php @@ -31,6 +31,7 @@ public function normalize(mixed $object, ?string $format = null, array $context $data['type'] = $object->getType(); $data['code'] = $object->getAttribute()->getCode(); $data['color'] = $object->getAttribute()->getColor(); + $data['weight'] = $object->getAttribute()->getWeight(); if (isset($context['translation']) && $context['translation'] instanceof TranslationInterface) { $translatedData = $object->getAttributeValueTranslation($context['translation']);