Doctrine2 lifecycleCallbacks prePersist not firing w/YAML config - doctrine-orm

All my Doctrine2 setups are done within YAML files. I have an entity class named LoanAppMenuProgress where I'm trying to execute a prePersist function. This LoanAppMenuProgress entity has a oneToOne relationship with another class named LoanApp. There is a foreign key association on the LoanAppMenuProgress table associated with the LoanApp table in the DB.
I have this config for my LoanAppMenuProgress class in LoanApp.LoanAppMenuProgress.orm.yml:
LoanEv\LoanAppBundle\Entity\LoanApp\LoanAppMenuProgress:
type: entity
repositoryClass: LoanEv\LoanAppBundle\Repository\LoanApp\LoanAppMenuProgress
table: loan_app_menu_progress
id:
id:
type: integer
generator: { strategy: auto }
### This is the OWNING side of the relationship
oneToOne:
loan_app:
targetEntity: LoanApp
inversedBy: loanapp_menu
joinColumn:
name: loan_id
referencedColumnName: id
fields:
loan_id:
type: integer
menu_id2:
type: integer
menu_id3:
type: integer
menu_id4:
type: integer
lifecycleCallbacks:
prePersist: [ updateMainMenuStatus ]
This is my LoanApp.LoanApp.orm.yml file:
LoanEv\LoanAppBundle\Entity\LoanApp\LoanApp:
type: entity
repositoryClass: LoanEv\LoanAppBundle\Repository\LoanApp\LoanAppRepository
table: loan_app
id:
id:
type: integer
generator: { strategy: auto }
## This is the INVERSE side of the relationship.
oneToOne:
loanapp_menu:
targetEntity: LoanAppMenuProgress
mappedBy: loan_app
fields:
bank_id:
type: integer
# etc.
In my LoanAppMenuProgress Entity class, I have the following code:
namespace LoanEv\LoanAppBundle\Entity\LoanApp;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Util\Debug;
/**
* LoanEv\LoanAppBundle\Entity\LoanApp\LoanAppMenuProgress
*/
class LoanAppMenuProgress
{
private $id;
private $loan_id;
/**
* #var LoanEv\LoanAppBundle\Entity\LoanApp\LoanApp
*/
private $loan_app;
private $menu_id2 = 0;
private $menu_id3 = 0;
private $menu_id4 = 0;
// ...
public function updateMainMenuStatus()
{
echo("Inside prePersist's updateMainMenuStatus function. ");
}
}
The following code is called from within my LoanAppController class:
// ...
//Save the menuStatus changes.
echo("About to persist. ");
$em->persist($menuStatus[0]);
echo("Done persisting.");
$em->flush();
// ...
When I execute the code in the LoanAppController the following gets written to my screen:
"About to persist. Done persisting."
I'm missing that bit in the middle where the output should read:
"About to persist. Inside prePersist's updateMainMenuStatus function. Done persisting."
The changes ARE getting written to the database, and all the functionality of the system is working as expected with the exception of the prePersist(). I've struggled with the yml setups for quite a while so my initial assumption is that my YAML setup is incorrect.
The documentation (as far as I could understand it) mentions that I should add the lifecycleCallbacks: and prePersist: items to the yml file, and then make sure I have a public function in the persisting Entity. Obviously, I'm missing something.
Does anyone have any ideas?
Thanks.

prePersist only gets called when you are performing an INSERT type statement. This event will never fire on an UPDATE action.
To perform some action when an entity is being UPDATEd, use preUpdate. Note, that preUpdate has far more restrictions on what can be performed with the entity in question.
Derrick

Related

Correctly show mapping association using inheritance

Im working with entities inheritances using doctrine Class Table Inheritance.
I have a base entity BaseEntity.
Then I have FirstEntity and SecondEntity that extends the BaseEntity.
In other hands, I have a StandardEntity that have a mapping association of OneToMany BaseEntity named baseEntities.
In the view page of an StandardEntity, it should also show the list of BaseEntity that is related to this one.
But I've got some trouble with that.
First error:
An exception has been thrown during the rendering of a template ("Warning: nl2br() expects parameter 1 to be string, object given").
First solution: I forced the type of the property to association.
- { property: baseEntities, type: association }
I've got a second error:
An exception has been thrown during the rendering of a template ("Notice: Undefined index: targetEntity").
But if I set the type to array, I get the correct item list but there is just simple text. I would like to have links that link to the related entity - FirstEntity or SecondEntity - view page.
- { property: baseEntities, type: array }
It is necessary to add the type "association" and type_options the class of the entity in question to also have the links, for example, here I use my class Tag
{ property: 'tags', type: 'association', type_options: 'App\Entity\Tag' }

Symfony2 phpunit tests failed with the error invalid mapping file in orm.yml

I am new in an project and we want to resume the stand of working still yet.
I found testfiles in project. These were a good point for me to have an entry and find out the things.
So let before say, this is a Symfony 2.8 project which uses fosuserbundle.
Back to my matter: when I execute phpunit in terminal so I get MappingExceptions from doctrine:
PHPUnit 5.1.3 by Sebastian Bergmann and contributors.
WWWWWWWWWWWWWWWWWWWWWWWWWWEWWWWWWEWWWWWWWWWWWWWWWWWWW 53 / 53 (100%)
Time: 1.4 seconds, Memory: 36.50Mb
There were 2 errors:
1) ******\*****Bundle\Tests\Controller\******ControllerTest::testIndex
Doctrine\Common\Persistence\Mapping\MappingException: Invalid mapping file '******.UserBundle.Entity.User.orm.yml' for class '*******\UserBundle\Entity\User'.
/var/www/*****vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php:86
/var/www/********/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php:117
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php:56
/var/www/********/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php:102
/var/www/*******/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:116
/var/www/*******/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php:332
/var/www/*******/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php:216
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:265
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php:67
/var/www/********/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php:50
/var/www/*******/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:665
/var/www/*******/vendor/friendsofsymfony/user-bundle/Doctrine/UserManager.php:40
It seems as something going wrong with User entity and related mapping file.
But these errors appear only when I execute phpunit. I deleted the user entity and orm file and run the commands generate entities and update schema. They work without any errors.
Here is the or.yml:
*****\UserBundle\Entity\User:
type: entity
table: fos_user
repositoryClass: ******\UserBundle\Entity\UserRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
confirm:
type: bigint
and here the entity class which extends from fos superclass user
<?php
namespace *****\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
*
*/
class User extends BaseUser
{
/**
* #var integer
*/
protected $confirm;
/**
* Set confirm
*
* #param integer $confirm
* #return User
*/
public function setConfirm($confirm)
{
$this->confirm = $confirm;
return $this;
}
/**
* Get confirm
*
* #return integer
*/
public function getConfirm()
{
return $this->confirm;
}
}
I am spending now the whole day with this issue but I can not figure out what is going wrong. It seems that for doctrine is the yml file invalid but why then it happens only with unittest?

Ignore a Doctrine2 Entity when running schema-manager update

I've got a Doctrine Entity defined that maps to a View in my database. All works fine, the Entity relations work fine as expected.
Problem now is that when running orm:schema-manager:update on the CLI a table gets created for this entity which is something I want to prevent. There already is a view for this Entity, no need to create a table for it.
Can I annotate the Entity so that a table won't be created while still keeping access to all Entity related functionality (associations, ...)?
Based on the original alswer of ChrisR inspired in Marco Pivetta's post I'm adding here the solution if you're using Symfony2:
Looks like Symfony2 doesn't use the original Doctrine command at:
\Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand
Instead it uses the one in the bundle:
\Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand
So basically that is the class that must be extended, ending up in having:
src/Acme/CoreBundle/Command/DoctrineUpdateCommand.php:
<?php
namespace App\Command;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class DoctrineUpdateCommand extends UpdateSchemaDoctrineCommand
{
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): ?int
{
$ignoredEntities = [
'App\Entity\EntityToIgnore',
];
$metadatas = array_filter($metadatas, static function (ClassMetadata $classMetadata) use ($ignoredEntities) {
return !in_array($classMetadata->getName(), $ignoredEntities, true);
});
return parent::executeSchemaCommand($input, $output, $schemaTool, $metadatas, $ui);
}
}
Eventually it was fairly simple, I just had to subclass the \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand into my own CLI Command. In that subclass filter the $metadatas array that's being passed to executeSchemaCommand() and then pass it on to the parent function.
Just attach this new subclassed command to the ConsoleApplication you are using in your doctrine cli script and done!
Below is the extended command, in production you'll probably want to fetch the $ignoredEntities property from you config or something, this should put you on the way.
<?php
use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class My_Doctrine_Tools_UpdateCommand extends UpdateCommand
{
protected $name = 'orm:schema-tool:myupdate';
protected $ignoredEntities = array(
'Entity\Asset\Name'
);
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui)
{
/** #var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
$newMetadata = [];
foreach ($metadatas as $metadata) {
if (!in_array($metadata->getName(), $this->ignoredEntities)) {
$newMetadata[] = $metadata;
}
}
return parent::executeSchemaCommand($input, $output, $schemaTool, $newMetadata, $ui);
}
}
PS: credits go to Marco Pivetta for putting me on the right track. https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/rwWXZ7faPsA
Quite old one but there is also worth nothing solution using Doctrine2: postGenerateSchema event listener - for me it's better than overriding
Doctrine classes:
namespace App\Doctrine\Listener;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
/**
* IgnoreTablesListener class
*/
class IgnoreTablesListener
{
private $ignoredTables = [
'table_name_to_ignore',
];
public function postGenerateSchema(GenerateSchemaEventArgs $args)
{
$schema = $args->getSchema();
$tableNames = $schema->getTableNames();
foreach ($tableNames as $tableName) {
if (in_array($tableName, $this->ignoredTables)) {
// remove table from schema
$schema->dropTable($tableName);
}
}
}
}
Also register listener:
# config/services.yaml
services:
ignore_tables_listener:
class: App\Doctrine\Listener\IgnoreTablesListener
tags:
- {name: doctrine.event_listener, event: postGenerateSchema }
No extra hooks is necessary.
In Doctrine 2.7.0 it was introduced the new SchemaIgnoreClasses entity manager config option that basically ignores the configured classes from any schema action.
To use it with Symfony we only need to add the schema_ignore_classes key in the Doctrine entity manager configuration like this:
doctrine:
dbal:
# your dbal configuration
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
schema_ignore_classes:
- Reference\To\My\Class
- Reference\To\My\OtherClass
$schema->getTableNames() was not working (I don't know why).
So:
<?php
namespace AppBundle\EventListener;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
class IgnoreTablesListener extends UpdateSchemaDoctrineCommand
{
private $ignoredEntities = [
'YourBundle\Entity\EntityYouWantToIgnore',
];
/**
* Remove ignored tables /entities from Schema
*
* #param GenerateSchemaEventArgs $args
*/
public function postGenerateSchema(GenerateSchemaEventArgs $args)
{
$schema = $args->getSchema();
$em = $args->getEntityManager();
$ignoredTables = [];
foreach ($this->ignoredEntities as $entityName) {
$ignoredTables[] = $em->getClassMetadata($entityName)->getTableName();
}
foreach ($schema->getTables() as $table) {
if (in_array($table->getName(), $ignoredTables, true)) {
// remove table from schema
$schema->dropTable($table->getName());
}
}
}
}
And Register a service
# config/services.yaml
services:
ignore_tables_listener:
class: AppBundle\EventListener\IgnoreTablesListener
tags:
- {name: doctrine.event_listener, event: postGenerateSchema }
Worked fine! ;)
If problem is only with producing errors in db_view, when calling doctrine:schema:update command, why not simplest way:
remove # from #ORM\Entity annotation
execute doctrine:schema:update
add # to ORM\Entity annotation
;-)

Single Table Inheritance and Yaml configuration

I want to use in my project Single Table Inheritance for symfony2/doctrine, but I can't find any working examples with yaml configuration for it. In official documentation there is only annotation configuration presented. I found xml examples, but I want to use yaml configuration. Can somebody help and share with some working code?
Okay built-in converter saves life.
In order to save time this an example of inheritance converted into yaml :
#file: Resources/config/doctrine/Person.orm.yml
Person:
type: entity
table: null
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
inheritanceType: SINGLE_TABLE
discriminatorColumn:
name: discr
type: string
length: 255
discriminatorMap:
person: Person
employee: Employee
lifecycleCallbacks: { }
#file: Resources/config/doctrine/Employee.orm.yml
Employee:
type: entity
table: null
lifecycleCallbacks: { }
Here is an example of YAML markup:
Entities config files should be put into src/Acme/StoreBundle/Resources/config/doctrine/<EntityName>.orm.yml according to reference.
Also built-in converter can be used: how to model inheritance in doctrine2 with yaml?

Nullable manyToOne relationship in Doctrine 2

I've setup a simple mapping.
manyToOne:
language:
nullable: true
targetEntity: Language
inversedBy: questions
The entity then generated has the following method
public function setLanguage(\Sf2MCQ\CoreBundle\Entity\Language $language)
{
$this->language = $language;
}
But now my question is how can unset a language since I can't do
setLanguage(null) ?
I'm using the adminBundle and that's what is he is trying to do so I don't know If I should rewrite the generated method or If I'm missing something.
You can unset the language if you modify your setter so that the method's argument has default null value.
public function setLanguage(\Sf2MCQ\CoreBundle\Entity\Language $language = null)
{
$this->language = $language;
}
Then $entity->setLanguage(null) works and null will be stored after persisting the entity.
More information about typehinting allowing null value, here:
http://php.net/manual/en/language.oop5.typehinting.php