Using Doctrine extension softdeleteable with api-platform - symfony

I'm building an API with Symfony 3.4 and api-platform. I want to use soft delete on my entity. I've installed DoctrineExtensions and StofDoctrineExtensionsBundle.
config.yml:
doctrine:
dbal:
connections:
default:
[…]
orm:
entity_managers:
default:
naming_strategy: doctrine.orm.naming_strategy.underscore
connection: default
mappings:
[…]
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
And my entity :
<?php
namespace AppBundle\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* MyEntity
*
* #ORM\Table(name="MyEntity", schema="MyEntity")
* #ORM\Entity(repositoryClass="AppBundle\Repository\MyEntityRepository")
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
* #ApiResource
*/
class MyEntity
{
/**
* #var \DateTime
* #ORM\Column(name="deleted_at", type="datetime")
*/
private $deletedAt;
[…]
This is not working. I'm aware that I need to configure something (namely the EventManager), but I don't know how.
Here is the error I get when I try to create an entity
Listener "SoftDeleteableListener" was not added to the EventManager!
I think I've done everything that page explains : StofDoctrineExtensionsBundle documentation
Any help would be greatly appreciated.

Try the following configuration at your config.yml
doctrine:
orm:
entity_managers:
default:
naming_strategy: doctrine.orm.naming_strategy.underscore
connection: default
mappings:
[…]
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
stof_doctrine_extensions:
default_locale: %locale%
orm:
default:
softdeleteable: true
Note: My configuration looks like:
orm:
auto_generate_proxy_classes: "%kernel.debug%"
entity_managers:
default:
auto_mapping: true
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
Seems that you're customizing your mappings so be sure you're autoloading the SoftDeleteable classes properly.

Related

Loggable doesn't write in the table

I want to use loogable extension, but it doesn't write anything in ext_log_enties table.
I'm using symfony 5.4 and Api-platform 2.6.8 in my project.
My configuration:
doctrine.yaml
doctrine:
dbal:
...
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
gedmo_loggable:
type: annotation
prefix: Gedmo\Loggable\Entity
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Loggable/Entity"
alias: GedmoLoggable # (optional) it will default to the name set for the mapping
is_bundle: false
...
stof_doctrine_extensions.yaml
stof_doctrine_extensions:
default_locale: fr_FR
orm:
default:
...
loggable: true
User.php
#[Gedmo\Loggable]
class User implements UserInterface
{
/**
* #ORM\Column(type="string", length=128, nullable=false)
*/
#[Gedmo\Versioned]
private ?string $firstName = null;
/**
* #ORM\Column(type="string", length=128, nullable=false)
*/
#[Gedmo\Versioned]
private ?string $lastName = null;
}
service.yaml :
services:
...
gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
What's wrong in my configuration ?

Why does Doctrine tries to insert new entities through relationships?

I have the following Doctrine entities where EntityA is the owning side:
<?php
class EntityA
{
/**
* #ORM\ManyToOne(targetEntity="EntityB" , inversedBy="propertyA")
*/
private $propertyB;
// ....
public function setEntityB(EntityB $entBRef)
{
$this->propertyB = $entBRef;
}
}
class EntityB
{
/**
* #ORM\OneToMany(targetEntity="EntityA", mappedBy="propertyB")
*/
private $propertyA;
// ....
}
I do also have a RepositoryB class:
<?php
class RepositoryB extends \Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository
{
public function __construct(\Doctrine\Common\Persistence\ManagerRegistry $registry)
{
parent::__construct($registry, EntityB::class);
}
}
Then in my class I am trying to persist an EntityA object:
<?php
class SomeService
{
/** #var RepositoryB */
private $repositoryB;
/** #var EntityManagerInterface */
private $entityManager;
public function __construct(RepositoryB $repositoryB, EntityManagerInterface $entityManager)
{
$this->repositoryB = $repositoryB;
$this->entityManager = $entityManager;
}
public function testSomething()
{
$entBRef = $this->repositoryB->find(1);
$newEnt = (new EntityA());
$newEnt->setEntityB($entBRef);
$this->entityManager->persist($newEnt);
$this->flush();
}
}
But I've got the following error:
A new entity was found through the relationship 'EntityA#propertyB'
that was not configured to cascade persist operations for entity:
EntityB. To solve this issue: Either explicitly call
EntityManager#persist() on this unknown entity or configure cascade
Here is how my doctrine.yaml file looks like:
parameters:
env(DATABASE_URL_WRITE): ''
services:
gedmo.listener.timestampable:
class: Org\CompBundle\Gedmo\Timestampable\TimestampableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ "#annotation_reader" ] ]
gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ "#annotation_reader" ] ]
gedmo.listener.deleteable:
class: Gedmo\SoftDeleteable\SoftDeleteableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ '#annotation_reader' ] ]
doctrine:
dbal:
default_connection: default
types:
microseconds: Org\CompBundle\DBAL\Types\DateTimeMicrosecondsType
connections:
read_only:
url: '%env(resolve:DATABASE_URL_READ)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
default:
url: '%env(resolve:DATABASE_URL_WRITE)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
orm:
auto_generate_proxy_classes: '%kernel.debug%'
default_entity_manager: default
entity_managers:
filters:
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
read_only:
connection: read_only
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
mappings:
gedmo_loggable:
type: annotation
prefix: Gedmo\Loggable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity"
alias: GedmoLoggable
is_bundle: false
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/vendor/org/comp-bundle/Entity'
prefix: 'Org\CompBundle\Entity'
alias: Drm
dql:
datetime_functions:
timetosec: DoctrineExtensions\Query\Mysql\TimeToSec
timediff: DoctrineExtensions\Query\Mysql\TimeDiff
now: DoctrineExtensions\Query\Mysql\Now
numeric_functions:
rand: DoctrineExtensions\Query\Mysql\Rand
default:
connection: default
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
gedmo_loggable:
type: annotation
prefix: Gedmo\Loggable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity"
alias: GedmoLoggable
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/vendor/org/comp-bundle/Entity'
prefix: 'Org\CompBundle\Entity'
alias: Drm
dql:
datetime_functions:
timetosec: DoctrineExtensions\Query\Mysql\TimeToSec
timediff: DoctrineExtensions\Query\Mysql\TimeDiff
now: DoctrineExtensions\Query\Mysql\Now
numeric_functions:
rand: DoctrineExtensions\Query\Mysql\Rand
Last but not least I do have the following setup at org\compbundle\Resources\services.yaml:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$logger: '#Psr\Log\LoggerInterface'
Org\CompBundle\Repository\:
resource: '../../Repository/{Case,Main}'
exclude: ['../../Repository/**/*Interface.php']
tags: ['doctrine.repository_service']
Org\CompBundle\Interfaces\Queues\QueueRepositoryInterface:
class: Org\CompBundle\Repository\DrmCase\QueueRepository
Org\CompBundle\Interfaces\Cases\CasesRepositoryInterface:
class: Org\CompBundle\Repository\DrmCase\CasesRepository
Doctrine\Common\Persistence\ManagerRegistry: '#doctrine'
What I have done so far?
Read a lot of post here on SO leading to the same solution add cascade={"persist"} to the owning side which I did and I must say did not work.
Read Doctrine docs looking for mistakes on my entities definition but so far everything looks fine to me.
I did found this which is helpful but I keep questioning myself why would Doctrine tries to insert and existent entity just because of the reference? Is there any way to get this working?
I do not want the EntityB persisted nor updated, it already exists. I do want the new EntityA have a FK to an existent record in EntityB.
During the weekend a work colleague did work very hard to find out what was causing the issue and after a lot of hours debugging how Doctrine and the UoW works he found where the problem was: how Doctrine and the Repository pattern works. Let me explain a little bit.
If you check this closer and go deep inside Doctrine you will notice how the following code leads to two different entities manager:
public function __construct(RepositoryB $repositoryB, EntityManagerInterface $entityManager)
{
$this->repositoryB = $repositoryB; // spin his own EM
$this->entityManager = $entityManager; // this is a new EM object
}
So:
$entBRef = $this->repositoryB->find(1);
Is being fetched through another EM and since I am using $this->entityManager to persist/flush the recently created entityA object then Doctrine sees $entBRef as a new entity that has to be persisted.
Our solution was to fetch everything through the same repository class which I do not like so much because we are querying things that does not belongs there and we had to ever persist/flush using the same EM.
If you have any other solution it is more than welcome.

Class LogEntry not found

In my app developed with Symfony 4.2 and API Platform,
I install DoctrineExtensions bundle to use Loggable extension,
and I follow instructions in Symfony documentation and Loggable extension.
When I only use one EntityManager, everything works.
But I wanna move the LogEntry entity table to another database.
So I created a second EntityManager as described in the Symfony doc.
After, I modify in my app an entity that has the annotation #Gedmo\Loggable
and I get this error:
The class 'Gedmo\Loggable\Entity\LogEntry' was not found in the chain configured namespaces 'App\Entity'.
My config/packages/doctrine.yaml file:
parameters:
env(DATABASE_URL): ''
doctrine:
dbal:
connections:
default:
driver: 'pdo_mysql'
server_version: '%env(resolve:SERVER_VERSION)%'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
url: '%env(resolve:DATABASE_URL)%'
log:
driver: 'pdo_mysql'
server_version: '%env(resolve:SERVER_VERSION)%'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
url: '%env(resolve:LOG_DATABASE_URL)%'
orm:
auto_generate_proxy_classes: '%kernel.debug%'
entity_managers:
default:
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
log:
naming_strategy: doctrine.orm.naming_strategy.underscore
connection: log
mappings:
gedmo_loggable:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity'
prefix: 'Gedmo\Loggable\Entity'
alias: GedmoLoggable
Example of Entity that I want to watch:
<?php
namespace App\Entity;
...
use Gedmo\Mapping\Annotation as Gedmo;
...
/**
* #ApiResource
* #Gedmo\Loggable
* #ORM\Table(name="patients")
*/
class Patient
{
/**
* #ORM\Id
* #ORM\Column(type="guid", unique=true)
* #ORM\GeneratedValue(strategy="UUID")
* #Assert\Uuid
*/
private $id;
/**
* #ORM\Column(length=50)
* #Assert\NotBlank()
* #Assert\Length(max=50)
* #Gedmo\Versioned
*/
public $familyName;
/**
* #ORM\Column(length=50)
* #Assert\NotBlank()
* #Assert\Length(max=50)
* #Gedmo\Versioned
*/
public $firstName;
...
}
I tried to add and remove some properties in the doctrine.yaml but I always get the same error...
Thanks for your time.

Symfony + Doctrine + MD5 issue

I want to use the sql function MD5 in my Symfony bundle so I added the file (https://gist.github.com/Basster/2774738) in \MyCompany\MyBundle\DQL\MD5Function.
Then I changed my config.yml file :
# app/config/config.yml
doctrine:
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# Added configuration for MD5 function
entity_managers:
default:
dql:
string_functions:
MD5: MyCompany\MyBundle\DQL\MD5Function
But I have the following error :
InvalidConfigurationException in ArrayNode.php line 309: Unrecognized options "naming_strategy, auto_mapping" under "doctrine.orm"
You mix the one entity manager configuration and the multi entity manager one.
You should use:
# app/config/config.yml
doctrine:
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# Added configuration for MD5 function
dql:
string_functions:
MD5: MyCompany\MyBundle\DQL\MD5Function
or:
# app/config/config.yml
doctrine:
orm:
entity_managers:
default:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# Added configuration for MD5 function
dql:
string_functions:
MD5: MyCompany\MyBundle\DQL\MD5Function
Alternatively you can create your own class to do that like explained here How to create and use custom built doctrine DQL function in symfony. Funny enough the example is based on SHA1 so you can just simply change the SH1 to MD5, like I did below.
Your custom MD5 class
namespace Football\FrontendBundle\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
class Md5 extends FunctionNode
{
public $value;
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->value = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $sqlWalker)
{
return 'MD5(' . $this->value->dispatch($sqlWalker) . ')';
}
}
Register it in config.yml
doctrine:
orm:
dql:
string_functions:
MD5: Football\FrontendBundle\DQL\Md5
And use it in your repository like this
namespace Football\FrontendBundle\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
class CountryRepository extends EntityRepository
{
public function findOneCountryBy($id)
{
return
$this
->createQueryBuilder('c')
->select('c, MD5(c.code) AS code')
->where('c.id = :id')
->setParameter('id', $id)
->getQuery()
->getSingleResult(Query::HYDRATE_SCALAR);
}
}

How can I log username and email address with StofDoctrineBundle?

I am running a Symfony2 app with Symfony2 and I'm using the FOSUserBundle to manage my users.
Now I implemented the StofDoctrineExtensionsBundle to use the loggable extensions. Unfortunately I cannot log the username and the email. If I try to activate logging via YML:
Acme\MyBundle\Entity\User:
type: entity
gedmo:
loggable: true
fields:
id:
...
username:
type: string
gedmo:
- versioned
email:
type: string
gedmo:
- versioned
I get a
Duplicate definition of column 'username' on entity in a field or discriminator column mapping.
So I tried to activate the logging in annotations (note that I define all my other entities via YML):
use FOS\UserBundle\Model\User as BaseUser;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #Gedmo\Loggable
*/
class User extends BaseUser {
...
/**
* #var string
* #Gedmo\Versioned
*/
protected $username;
/**
* #var string
* #Gedmo\Versioned
*/
protected $email;
But: Nothing happens. All other properties of my User are logged, but username and email are not.
What do I have to change in order to activate logging for those both properties? Or is this an interdependency between the FOSUserBundle and the StofDoctrineExtensionsBundle that I cannot influence?
Have you added this configuration?:
//app/config/config.yml
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
mappings:
translatable:
type: annotation
alias: Gedmo
prefix: Gedmo\Translatable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
loggable:
type: annotation
alias: Gedmo
prefix: Gedmo\Loggable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity"
tree:
type: annotation
alias: Gedmo
prefix: Gedmo\Tree\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
sluggable:
type: annotation
alias: Gedmo
prefix: Gedmo\Sluggable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"

Resources