I've got an entity called Logs
that has a ManyToOne relation to an HourlyRates
entity. Both Logs
and HourlyRates
have date properties. When adding a log
with a specific date, an hourlyRate
is assigned to it if the log-date fits within the rate's time range. I'm using the Doctrine Extensions Bundle, so the data in each entity can be soft-deleted.
What needs to be done:
After soft-deleting an HourlyRate
the related Log
has to be updated, so that the nearest existing past HourlyRate
takes the place of the deleted one.
I tried to use preSoftDelete
, postSoftDelete
, preRemove
and postRemove
methods inside an HourlyRate
entity listener. The code was being executed and the setters were working properly, but the database hasn't been updated in any of said cases. An "EntityNotFoundException" was being thrown everytime.
My second approach was to use the preRemove
event along with setting the cascade
option to "all" by using annotations in the HourlyRate
class. As a result, soft-deleting an hourlyRate
caused soft-deleting of the related log
.
The Log entity:
class Log
{
use SoftDeleteableEntity;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\HourlyRate", inversedBy="logs")
* @ORM\JoinColumn(nullable=false)
*/
private $hourlyRate;
public function setHourlyRate(?HourlyRate $hourlyRate): self
{
$this->hourlyRate = $hourlyRate;
return $this;
}
}
The HourlyRate entity:
class HourlyRate
{
use SoftDeleteableEntity;
//other code
/**
* @ORM\OneToMany(targetEntity="App\Entity\Log", mappedBy="hourlyRate", cascade={"all"})
*/
private $logs;
}
The HourlyRate entity listener:
class HourlyRateEntityListener
{
public function preRemove(HourlyRate $hourlyRate, LifecycleEventArgs $args)
{
$entityManager = $args->getObjectManager();
/** @var HourlyRateRepository $HRrepo */
$HRrepo = $entityManager->getRepository(HourlyRate::class);
foreach ($hourlyRate->getLogs() as $log)
{
$rate = $HRrepo->findHourlyRateByDate($log->getDate(), $log->getUser(), $hourlyRate);
$log->setHourlyRate($rate);
}
}
}
The repository method:
class HourlyRateRepository extends ServiceEntityRepository
{
public function findHourlyRateByDate(?\DateTimeInterface $datetime, User $user, ?HourlyRate $ignore = null): ?HourlyRate
{
$qb = $this->createQueryBuilder('hr')
->where('hr.date <= :hr_date')
->andWhere('hr.user = :user')
->orderBy('hr.date', 'DESC')
->setMaxResults(1)
->setParameters(array('hr_date' => $datetime, 'user' => $user));
//ignore the "deleted" hourlyRate
if($ignore){
$qb->andWhere('hr.id != :ignored')
->setParameter('ignored', $ignore->getId());
}
return $qb->getQuery()
->getOneOrNullResult()
;
}
}
Thank you in advance for any of your help.
EDIT:
Okay so after a whole week of trials and errors i finally managed to achieve the result I wanted.
I removed the One-To-Many relation between the hourlyRates
and the logs
from the entities, but left the $hourlyRate
property inside the Log
class. Then I got rid of the HourlyRateEntityListener
and the preRemove()
method from the LogEntityListener
. Instead, I implemented the postLoad()
method:
class LogEntityListener
{
public function postLoad(Log $log, LifeCycleEventArgs $args)
{
$entityManager = $args->getObjectManager();
$HRrepo = $entityManager->getRepository(HourlyRate::class);
/** @var HourlyRateRepository $HRrepo */
$rate = $HRrepo->findHourlyRateByDate($log->getDate(), $log->getUser());
$log->setHourlyRate($rate);
}
}
This approach allows me to set the proper hourlyRate
for each log
without involving the database. Idk if this solution is acceptable though.