How to auto update properties of a doctrine entity? - symfony

I've created a doctrine entity class in my symfony 2 project and now I want to auto set a property before saving by the entity manager. Are there some hooks to implement or how can I solve my issue?

Yes, you can:
http://symfony.com/doc/master/book/doctrine.html#lifecycle-callbacks
<?php
namespace MyNS\Dummy;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Dummy
{
protected $property;
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function automaticProperty()
{
$this->property = 'whatever';
}
}

Related

Symfony Custom Repository class not working

I have a class
/**
* #ORM\Table(name="registration_number")
* #ORM\Entity
* #ORM\Entity(repositoryClass="PNC\MISDashboardBundle\Repositories\RegistrationNumberRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\Entity#EntityListeners({"RegistrationNumberListener"})
*/
class RegistrationNumber {
}
and the repo class
namespace PNC\MISDashboardBundle\Repositories;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
/**
* RegistrationNumberRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class RegistrationNumberRepository extends EntityRepository {
public function findByTotalMatches($keyword)
{
/* your awesome code block */
return 34;
}
}
and I am calling the method in this way;
$check = $em->getRepository('PNCMISDashboardBundle:RegistrationNumber')
->findTotalMatches(5);
But it says that;
Undefined method 'findTotalMatches'. The method name must start with
either findBy or findOneBy!
I have built lot of other custom repo and works, i don't know that wrongs with this one. has anyone any hint what is wrong with this.
As said in comment,
Change :
/**
* #ORM\Table(name="registration_number")
* #ORM\Entity
* #ORM\Entity(repositoryClass="PNC\MISDashboardBundle\Repositories\RegistrationNumberRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\Entity#EntityListeners({"RegistrationNumberListener"})
*/
class RegistrationNumber {
To :
/**
* #ORM\Table(name="registration_number")
* #ORM\Entity(repositoryClass="PNC\MISDashboardBundle\Repositories\RegistrationNumberRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\EntityListeners({"RegistrationNumberListener"})
*/
class RegistrationNumber {
And it should works.

SonataUserBundle oneToMany Mapping Error

I dont know what else to try, this seems like it should be so straight forward, but the Symfony profiler continues to show this error:
Here are my User and Venue classes:
User:
namespace Application\Sonata\UserBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Venue", mappedBy="user")
*/
protected $venues;
public function __construct()
{
parent::__construct();
$this->venues = new ArrayCollection();
}
}
Venue:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Venue
*
* #ORM\Table()
* #ORM\Entity
*/
class Venue
{
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="venues")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
It seems to work fine in the application, I only get the error inside the Symfony Profiler. It keeps telling me venues doesnt exist on User but Im looking right at it! What am I not doing correctly here?

Symfony2 Relationships using Interfaces results in duplicated table

I'm trying to relate an entity in one bundle with another in another bundle to make the second one independent from the first one, and be able to reuse it.
I'm following this documentation and this StackOverflows answer.
In the reusable bundle I have a Folder, File a that belongs to the folder and and interface like this:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class Folder implements FolderInterface
{
// Has many files
}
namespace Acme\FolderBundle\Entity;
interface FolderInterface
{
// no methods here
}
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class File
{
// Belongs to one folder
}
And on the other bundle just one class:
namespace Acme\NewBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Acme\FolderBundle\Entity\Folder as BaseFolder;
use Acme\FolderBundle\Entity\FolderInterface;
/**
* #ORM\Entity
*/
class Folder extends BaseFolder implements FolderInterface
{
// Has many files
}
And the config.yml's ORM configuration:
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
resolve_target_entities:
Acme\FolderBundle\Entity\FolderInterface: Acme\NewBundle\Entity\Folder
If I try to update my database schema, I get the following error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'foldersDatabase.folder' already exists.
To get this working, I have to explicitly change one of the Folder's Entities table:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
* #ORM\Table(name="distributed_folder")
*/
class Folder implements FolderInterface
{
// Has many files
}
Then, everything works but I get stuck with a table in my database (distributed_folder) that is never used.
Thanks a lot in advance!!
EDIT:
Fixed the annotation in the FolderInterface
You can not make one entity extend another entity this way.
If you want to have an abstract class which contains the fields for two or more subclass entities, you should mark the abstract class as #ORM\MappedSuperclass , and make sure, it will not have the annotation #Entity. While on the subclasses , they each should have #Entity annotation , and #Table annotation with a unique name attribute.
Here is an example :
<?php
namespace Radsphere\MissionBundle\Model\Core\BaseAbstract;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*
* An abstract class implementation of mission
*/
abstract class AbstractMission implements MissionInterface, IntegratedPluginInterface
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=36, unique=true)
*/
protected $guid;
/**
* #ORM\Column(type="string", length=255)
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="MissionTask", cascade={"persist", "remove"})
* #ORM\JoinTable(name="mtm_mission_task",
* joinColumns={#ORM\JoinColumn(name="mission_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
protected $tasks;
/**
* {#inheritDoc}
*/
public function addTask(MissionTaskInterface $missionTask)
{
$this->getTasks()->add($missionTask);
$missionTask->setMission($this);
}
/**
* {#inheritDoc}
*/
public function setTasks(Collection $tasks)
{
/** #var MissionTaskInterface $task */
foreach ($tasks as $task) {
$task->setMission($this);
}
$this->tasks = $tasks;
}
/**
* {#inheritDoc}
*/
public function getTasks()
{
$tasks = $this->tasks;
foreach ($tasks as $task) {
if ($task instanceof MissionTaskInterface) {
if (!$task->getIsEnabled()) {
/** #var $tasks Collection */
$tasks->removeElement($task);
}
}
}
return $tasks;
}
}
and the entity itself:
<?php
namespace Radsphere\MissionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Radsphere\MissionBundle\Model\Core\BaseAbstract\AbstractMission;
/**
* Mission entity
*
* #ORM\Table(name="mission_bundle_mission", indexes={#ORM\Index(name="guid_idx", columns={"guid"})})
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="MissionRepository")
*/
class Mission extends AbstractMission
{
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new ArrayCollection();
}
}

Symfony2 - Inheritance - repository find method

I have entities
<?php
namespace Proj\Bundle\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* ClassTop
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Proj\Bundle\MyBundle\Repository\ClassTopRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="aVal", type="string")
* #ORM\DiscriminatorMap({ "ValOne" = "ClassSubOne", "ValTwo" = "ClassSubTwo", "ValThree" = "ClassSubThree" })
*
*/
class ClassTop
{
.....
}
class ClassSubOne extends ClassTop
{
....
}
class ClassSubTwo extends ClassTop
{
....
}
class ClassSubThree extends ClassTop
{
....
}
My problem is when I call the find() (or findOneBy, findAll, etc) method, I have an Oracle error.
$entityManager->getRepository('ProjMyBundle:ClassSubOne')->findAll()
In the query I have
SELECT field, field2 FROM CLASSTOP WHERE aVAL IN ()
The discriminator value "ValOne" is not mapping and not passed in Doctrine.
Any help would be much appreciated.
You need to repository classes for the subclasses not the top one. You treat your subclasses as full defined classes and that's all. Then when you need to query for your entities you don't go for the top entity but the subclasses. I don't know if I made myself clear here!
In your case:
#ORM\Entity(repositoryClass="path\to\your\repository")
The repository line goes for the subclasses not the top superclass. so you do
/**
*
* #ORM\Entity(repositoryClass="path\to\your\ClassSubOneRepository")
*/
class ClassSubOne extends ClassTop
{
...
}
instead of
/**
*
* #ORM\Entity(repositoryClass="Proj\Bundle\MyBundle\Repository\ClassTopRepository")
*/
class ClassTop
{
...
}

Symfony2 custom validator annotation not working

I have a custom validator in src/VNN/PressboxBundle/Component/Validator/CurrentPasswordValidator.php:
<?php
namespace VNN\PressboxBundle\Component\Validator\Constraints;
use Symfony\Component\Validator\ConstraintValidator,
Symfony\Component\Validator\Constraint,
Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface,
Symfony\Component\Security\Core\SecurityContextInterface,
JMS\DiExtraBundle\Annotation\Validator,
JMS\DiExtraBundle\Annotation\InjectParams,
JMS\DiExtraBundle\Annotation\Inject;
/**
* #Validator("user.validator.current_password")
*/
class CurrentPasswordValidator extends ConstraintValidator
{
// ...
}
And then I have this at src/VNN/PressboxBundle/Component/Validator/Contraints/CurrentPassword.php:
<?php
namespace VNN\PressboxBundle\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class CurrentPassword extends Constraint
{
public $message = "Your current password is not valid";
/**
* #return string
*/
public function validatedBy()
{
return 'user.validator.current_password';
}
}
For some reason, when I try to add an annotation that uses this validator, I get an error. Here's my entity/annotation:
<?php
namespace VNN\PressboxBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinTable as JoinTable;
use Doctrine\ORM\Mapping\JoinColumn as JoinColumn;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContext;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MaxLength;
use Symfony\Component\Validator\Constraints\Email;
use VNN\PressboxBundle\Component\Validator\Constraints\CurrentPassword;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* VNN\PressboxBundle\Entity\User
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User implements UserInterface, \Serializable
{
/**
* #var string $password
* #Assert\NotBlank()
* #CurrentPassword()
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
}
The error I'm getting is:
AnnotationException: [Semantical Error] The annotation
"#VNN\PressboxBundle\Component\Validator\Constraints\CurrentPassword"
in property VNN\PressboxBundle\Entity\User::$password does not exist,
or could not be auto-loaded.
What am I doing wrong?
Check that VNN\PressboxBundle\VNNPressboxBundle() is correctly registered with the kernel in AppKernel.php.
Update: If it is correctly registered, and src/ is registered as a fallback with the autoloader (assuming VNN/ is under src/), you may want to try and load the service yourself manually and check the stack trace if/when it fails:
$validator = $container->get('user.validator.current_password');
// Pretending this is a unit test:
// $this->assertInstanceOf('VNN\PressboxBundle\Component\Validator\Constraints\CurrentPasswordValidator', $validator);
Also, make sure you have cleared the cache (if not testing in dev environment).
Side note (but unrelated to your problem) - if you start to add more validators, it will be more useful to use this syntax:
<?php
namespace VNN\PressboxBundle\Entity;
use VNN\PressboxBundle\Component\Validator\Constraints as VNN;
class User implements UserInterface, \Serializable
{
/**
* #VNN\CurrentPassword
*/
private $password;
}

Resources