c33s/doctrine-extra

A Collection of Traits which can be used to build a Doctrine Entity.


README

A Collection of Traits which can be used to build a Doctrine Entity.

Example Entity Customer:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use C33s\Entity\Traits\Field;
use C33s\Entity\Traits\Behaviour;
use C33s\Entity\Embeddable\CustomerNumber;
use Gedmo\Timestampable\Traits\TimestampableEntity;

class Customer
{
    use Field\RequiresId;
    use Field\RequiresUniqueName;
    use Field\RequiresEmail;
    use Field\HasDescription;
    use Field\HasIsActive;
    use Behaviour\IsTimestampable;
    use Behaviour\IsPublishable;
    use TimestampableEntity;
}

Requirements

Minimum Php version is 7.1

Custom Types Abstract Classes

if you prefer to work with value objects you can easily define new doctrine orm types (like string) with the available abstract types. there are three abstract types you can use:

  • AbstractValueObjectJsonType for using with objects which can be serialized into arrays/json
  • AbstractValueObjectStringType used for simple string value objects like for a name
  • AbstractValueObjectTextType the same as AbstractValueObjectStringType but extends Doctrines TextType to use the correct database field for text.

if you want to create a custom type address to put into your Person entity

first create a value object for your address (which can of course contain other objects). it must implement the array interface so the data can be converted to an array.

src/Value/Address.php:


use C33s\Doctrine\Interfaces\ArrayInterface;

final class Address extends ArrayInterface
{
    private AddressLine $streetLine1;
    private AddressLine $streetLine2;
    private City $city;
    private Zip $zip;
    private Province $province;
    private CountryCode $country;
}

then create your custom type where the name is the name of the type and valueObject class is the class it should be mapped to.

src/ORM/Types/AddressType.php:

namespace App\ORM\Types;

use App\Value\Address;
use C33s\Doctrine\Types\AbstractValueObjectJsonType;

class AddressType extends AbstractValueObjectJsonType
{
    protected $name = 'address';
    protected $valueObjectClass = Address::class;
}

App\Entity\Person.php:

class Person
{
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\Column(type="address")
     */
    private $address;
    
    public function getAddress(): Address
    {
        return $this->address;    
    }
      
}

config/../doctrine.yaml:

doctrine:
    dbal:
        types:
            address: App\ORM\Types\AddressType

Fields

Basic Definitions

  • Embeds... -> `@ORM\Embedded(class="...")
  • Has... -> @ORM\Column(type="...") & getProperty(): ?type & setProperty(?type $property)
  • Requires... -> @ORM\Column(type="...", nullable=false) & @Assert\NotBlank() & getProperty(): ?type & setProperty(type $property)
  • RequiresUnique... -> @ORM\Column(type="...", unique=true, nullable=false) & @Assert\NotBlank() & getProperty(): ?type & setProperty(type $property)

Requires Traits use

  • @var Type|null for ease of use with __construct($name=null) defaults
  • getProperty(): ?type because the function may be called before the property is set - for example in EasyAdminBundle when adding a new entry

Available Fields

  • HasDate
  • HasDescription
  • HasDisplayName
  • HasEmail
  • HasEmbeddedEmail
  • HasFaxNumber
  • HasFirstName
  • HasIsActive
  • HasIsEnabled
  • HasIsPublished
  • HasIsVisible
  • HasLastName
  • HasPhoneNumber
  • HasPostTitle
  • HasPreTitle
  • HasRank
  • HasRoles
  • HasSlugFromName
  • HasUserSerializer
  • RequiresDate
  • RequiresDescription
  • RequiresEmail
  • RequiresFaxNumber
  • RequiresFirstName
  • RequiresId
  • RequiresLastName
  • RequiresPassword
  • RequiresPhoneNumber
  • RequiresPostTitle
  • RequiresRank
  • RequiresUrl
  • RequiresUniqueName
  • RequiresUniqueUrl
  • RequiresUniqueUsername
  • EntityHasUploadableImage: found in src/Entity/Traits/Field/edition -> copy to src/Entity/Traits/Field/
  • Replace Trait name to reflect the name of your Entity (like ProductHasUploadableImage)
  • change namesapce
  • adjust mapping(currently entity_image)
     * @Vich\UploadableField(mapping="entity_image", fileNameProperty="imageName", size="imageSize")
    
  • create vich_uploader configuration for your mapping
    vich_uploader:
    mappings:
      entity_image:
    # see config/app/vich_uploader.yaml or config/edition/vich_uploader.yaml for examples
    

Behavior

  • IsBlameable
  • IsFeatureFlagable
  • IsPublishable
  • IsTimestampable

Method

  • HasDummyAdvancedUserInterface
  • HasDummySalt
  • HasParentAndChildren

General

  • UploadableTypeEmbeddedTrait

RequirePassword & User Entity & UserBundle

we need an entity called LoginUser to work with this lib out of the box. in the user bundle RequirePassword is used. The user entity should implement Serializable, EquatableInterface, UserInterface

RequiresPassword is a little bit tricky because of the requirement of hashed/crypted passwords. we have two fields password and plainPassword. the password field is the only mapped field. plainPassword is only used to set a plain password which will be auto-encoded using an event listener.

Overriding Metadata of Traits

this will override length and all @Assert. so in this case no @Assert\NotBlank from the Trait. this works out of the box without the

<?php


namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use C33s\Doctrine\Entity\Traits\Field;

/**
 * @ORM\Entity()
 * @codeCoverageIgnore
 */
class OverrideTest
{
    use Field\RequiresId;
    use Field\RequiresEmail;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=10, unique=true)
     * @Assert\Email()
     */
    protected $email;
}

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/override-field-association-mappings-in-subclasses.html https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/inheritance-mapping.html https://stackoverflow.com/questions/32571920/overriding-doctrine-trait-properties https://andy-carter.com/blog/overriding-extending-a-php-trait-method