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

AttributeReader reads attributes from class traits #2835

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

dgrothaus-mc
Copy link

This allows using traits in Entities to configure settings with attributes.

This change vastly enhances the usability for cases where a custom translation table is used. Instead of having to add an annotation to every entity with a translatable field to configure the usage of a custom translation table, it's sufficient to write a Trait that has the annotation.

Example:

App\Entity\Traits\HasTranslationTrait.php
<?php

namespace App\Entity\Traits;

use App\Entity\Translation;
use Gedmo\Mapping\Annotation as Gedmo;

/**
 * This trait handles relations between the translation table and foreign keys referencing that table.
 *
 */
#[Gedmo\TranslationEntity(class: Translation::class)]
trait HasTranslationTrait
{
    #[Gedmo\Locale]
    private ?string $locale = null;

    public function setLocale(string $locale): static
    {
        $this->locale = $locale;

        return $this;
    }
}
App\Entity\Translation.php
<?php

namespace App\Entity;

use App\Repository\TranslationRepository;
use Doctrine\ORM\{Mapping as ORM, Mapping\Entity, Mapping\Index, Mapping\Table, Mapping\UniqueConstraint};
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation;

#[Entity(repositoryClass: TranslationRepository::class)]
#[Table(name: 'my_translation')]
#[Index(columns: ['locale', 'object_class', 'foreign_key'], name: 'translation_lookup_idx')]
#[Index(columns: ['object_class', 'foreign_key'], name: 'translation_general_idx')]
#[UniqueConstraint(name: 'translation_unique_idx', columns: ['locale', 'object_class', 'field', 'foreign_key'])]
#[Index(columns: ['content'], name: 'translation_content_ftidx', flags: ['fulltext'])]
#[Index(columns: ['additional_field'], name: 'translation_additional_field_idx')]
class Translation extends AbstractTranslation
{
    #[ORM\Column(
        name: 'additional_field'
    )]
    protected ?string $additionalField = null;

    /**
     * Get additional field.
     *
     * @return ?string
     */
    public function getAdditionalField(): ?string
    {
        return $this->additionalField;
    }
}
App\Repository\TranslationRepository.php
<?php

namespace App\Repository;

use App\Entity\Translation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Gedmo\Translatable\Entity\Repository\TranslationRepository as GedmoTranslationRepository;

/**
 * @extends GedmoTranslationRepository<Translation>
 *
 * @method Translation|null find($id, $lockMode = null, $lockVersion = null)
 * @method Translation|null findOneBy(array $criteria, array $orderBy = null)
 * @method Translation[]    findAll()
 * @method Translation[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class TranslationRepository extends GedmoTranslationRepository
{
    public function __construct(EntityManagerInterface $em, ClassMetadata $class)
    {
        parent::__construct($em, $class);
    }

    public function findObjectByAdditionalField($field, $value, $class): ?object
    {
        // Custom Query for additional field here.
    }
}

Without this change, one has to add the attribute to use the Translation table to every Entity having a translatable field. With this change applied, it's sufficient to use the HasTranslationTrait to use the custom table as well as having the benefit that the field for the locale with the proper attribute is already added.

This allows the usage of traits in Entities to configure settings with
attributes.
@mbabker
Copy link
Contributor

mbabker commented Jul 2, 2024

My first thought is I'm not 100% sure about this as it changes the behavior for every class-level attribute. To my knowledge, nobody with an attribute-backed API is reading class attributes from traits like this, and I think I'd find it a little surprising for an attribute declared on the class-level of a trait to get arbitrarily applied to any consuming class (or if they are then I've definitely never seen it in use).

@dgrothaus-mc
Copy link
Author

I do understand that concern.

When I think about it, I wouldn't be sure about how to handle conflicting attributes.

My initial idea was, that it felt strange to put all translation related fields in a trait and that works, but having to put the actual table for the translation in every Entity instead of the trait.

I see some error potential when the trait is added to an entity, but the table is not defined in the entity, that the fields being defined by the trait are not present in the default translation table.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants