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 ?
Related
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.
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.
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.
I had some trouble extending user admin when i tried to use the classes in my bundle and not in the generated bundle (Application/Sonata/UserBundle).
I don't know if my solution is good but it works for me.
Here is the procedure:
First the config:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
framework:
#esi: ~
#translator: { fallback: "%locale%" }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
fragments: ~
http_method_override: true
# Twig Configuration
twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"
# Assetic Configuration
assetic:
debug: "%kernel.debug%"
use_controller: false
bundles: [ ]
#java: /usr/bin/java
filters:
cssrewrite: ~
#closure:
# jar: "%kernel.root_dir%/Resources/java/compiler.jar"
#yui_css:
# jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"
# Doctrine Configuration
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"
# added for sonata user
types:
json: Sonata\Doctrine\Types\JsonType
orm:
auto_generate_proxy_classes: "%kernel.debug%"
# added for sonata user
entity_managers:
default:
mappings:
SonataUserBundle: ~
FOSUserBundle: ~
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: "%mailer_transport%"
host: "%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
spool: { type: memory }
# FosRest configuration
fos_rest:
view:
formats:
rss: false
xml: false
templating_formats:
html: true
force_redirects:
html: true
failed_validation: HTTP_BAD_REQUEST
default_engine: twig
# SonataAdmin configuration
sonata_block:
default_contexts: [cms]
blocks:
# Enable the SonataAdminBundle block
sonata.admin.block.admin_list:
contexts: [admin]
sonata.user.block.menu: # used to display the menu in profile pages
sonata.user.block.account: # used to display menu option (login option)
sonata_user:
security_acl: false
manager_type: orm # can be orm or mongodb
class:
user: Wf\Bundle\TestsBundle\Entity\User
group: Wf\Bundle\TestsBundle\Entity\Group
admin:
user:
class: Wf\Bundle\TestsBundle\Admin\UserAdmin
controller: SonataAdminBundle:CRUD
translation: SonataUserBundle
group:
class: Wf\Bundle\TestsBundle\Admin\GroupAdmin
controller: SonataAdminBundle:CRUD
translation: SonataUserBundle
fos_user:
db_driver: orm # can be orm or odm
firewall_name: main
user_class: Wf\Bundle\TestsBundle\Entity\User
# use this option when easy extending the bundle (app/console sonata:easy-extends:generate SonataUserBundle --dest=src)
#user_class: Sonata\UserBundle\Entity\BaseUser
group:
group_class: Wf\Bundle\TestsBundle\Entity\Group
#group_class: Sonata\UserBundle\Entity\BaseGroup
group_manager: sonata.user.orm.group_manager
service:
user_manager: sonata.user.orm.user_manager
UserAdmin class; same with GroupAdmin class
<?php
/**
* Created by razvan.
* Date: 1/16/15
* Time: 1:53 PM
*/
namespace Wf\Bundle\TestsBundle\Admin;
use Sonata\UserBundle\Admin\Model\UserAdmin as BaseUserAdmin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use FOS\UserBundle\Model\UserManagerInterface;
use Sonata\AdminBundle\Route\RouteCollection;
class UserAdmin extends BaseUserAdmin
{
/**
* {#inheritdoc}
*/
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->with('General')
->add('username')
->add('email')
->end()
// .. more fields
;
}
/**
* {#inheritdoc}
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('User data')
->add('username')
->add('email')
->add('plainPassword', 'text', array('required' => false))
->end()
// .. more fields
;
if (!$this->getSubject()->hasRole('ROLE_SUPER_ADMIN')) {
$formMapper
->with('Management')
->add('roles', 'sonata_security_roles', array(
'expanded' => true,
'multiple' => true,
'required' => false
))
->add('locked', null, array('required' => false))
->add('expired', null, array('required' => false))
->add('enabled', null, array('required' => false))
->add('credentialsExpired', null, array('required' => false))
->end()
;
}
}
/**
* {#inheritdoc}
*/
protected function configureDatagridFilters(DatagridMapper $filterMapper)
{
$filterMapper
->add('id')
->add('username')
->add('locked')
->add('email')
;
}
/**
* {#inheritdoc}
*/
protected function configureListFields(ListMapper $listMapper)
{
// overide defaults
}
}
User class; same wirh Group class
<?php
namespace Wf\Bundle\TestsBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* Class User
* #package Wf\Bundle\TestsBundle\Entity
*
* #ORM\Table(name="fos_user_user")
* #ORM\Entity()
*/
class User extends BaseUser
{
/**
* #var integer $id
*
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* Get id
*
* #return integer $id
*/
public function getId()
{
return $this->id;
}
}
Now in Resources\config create a folder named "doctrine" and then add the folowing files:
First file is named Group.orm.xml
Inser the following into the file
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Wf\Bundle\TestsBundle\Entity\Group" table="fos_user_group">
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
</entity>
</doctrine-mapping>
Second file is User.orm.xml
Insert the following:
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Wf\Bundle\TestsBundle\Entity\User" table="fos_user_user">
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
</entity>
</doctrine-mapping>
Be careful to change the path to your own and clear the cache.
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"