stack at Doctrine PostPersist event listener - symfony

its my VisitorController Class
public function chooseVisitor($zone)
{
$em = $this->getDoctrine()->getManager();
$lvisitor = $em->getRepository('EMRSabaBundle:Personel')->findOneBy(array('status' => '1', 'lastv' => '1', 'zone' => $zone));
$id = $lvisitor->getId();
$lvisitor->setLastv(0);
$newvisitorid = $em->getRepository('EMRSabaBundle:Personel')->findNewVisitor($id);
$newvisitorid = $newvisitorid[0];
$newvisitor = $em->getRepository('EMRSabaBundle:Personel')->find($newvisitorid);
$newvisitor->setLastv(1);
$em->flush();
return $newvisitor;
}
public function defineZone($Customer)
{
$phone = substr($Customer,1);
switch ($phone)
{
case 2:
$zone = 1;
break;
case 4:
$zone = 1;
break;
case 8:
$zone = 2;
break;
case 7:
$zone = 3;
break;
}
return $zone;
}
my EventListener
namespace EMR\SabaBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use EMR\SabaBundle\Entity\Orders;
use EMR\SabaBundle\Entity\Visit;
use EMR\SabaBundle\Controller\VisitController;
class EntityListener {
public function postPersist(LifecycleEventArgs $args)
{
$orders = $args->getEntity();
$em = $args->getEntityManager();
if ($orders instanceof Orders){
if ($orders->getStatus() == 1){
$VC = new VisitController();
$zone = $VC->defineZone($orders->getCustomer()->getId());
$personel = $VC->chooseVisitor($zone);
$visit = new Visit();
$visit->setDate(new \DateTime());
$visit->setStatus(0);
$visit->setOrders($orders);
$visit->setPersonel($personel);
$em->persist($visit);
$em->flush();
}
}
}
}
& my services.yml
entity.listener:
class: EMR\SabaBundle\EventListener\EntityListener
tags:
- { name: doctrine.event_listener, event: postPersist }
i want to create & persist new visit when order entity status is 1
i dont know where is the problem & how to debug the event listener
Could anyone give me a hand to accomplish this job

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#postupdate-postremove-postpersist
postUpdate, postRemove, postPersist
The three post events are called inside EntityManager#flush(). Changes
in here are not relevant to the persistence in the database, but you
can use these events to alter non-persistable items, like non-mapped
fields, logging or even associated classes that are directly mapped by
Doctrine.
Try prePersist event.

Related

Gedmo Translatable persist default translation

I have a website built with Symfony 2.8 and Gedmo Translatable.
In order to use HINT_INNER_JOIN and filter items which don't have a translation I had to set persist_default_translation to true:
stof_doctrine_extensions:
default_locale: '%locale%' # TODO: what does it happen when removing this line?
translation_fallback: true
persist_default_translation: true
orm:
default:
timestampable: true
blameable: true
translatable: true
Unfortunately this caused that my existing translations for the default language are no more persisted (and they appear empty).
I would need to force re-save all of my entities to generate the default locale again.
How can I do that? I tried with clone and persist but it creates a duplicate of the entity.
Is it possible to force Doctrine to update all the fields again?
I ended up creating a custom command to migrate all the translations. I created a fake translation called "kr" and then updated all the record with "kr" to "fr" with an SQL query.
I used reflection and other "black magic" to get the properties with the Translatable annotation, maybe this could help someone with the same problem. Here is the code:
class NormalizeTranslationsCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
// the name of the command (the part after "app/console")
->setName('app:normalize-translations')
// the short description shown while running "php app/console list"
->setDescription('Normalizes the translations.')
// the full command description shown when running the command with
// the "--help" option
->setHelp('This command allows you to normalize the translations...')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// all the translatable classes
$classes = [
Entities\MyClass1::class,
Entities\MyClass2::class,
];
foreach ($classes as $class) {
$this->processClass($class, $output);
}
}
private function processClass($class, $output)
{
$output->writeln(sprintf('Processing class <info>%s</info>', $class));
// gets all the properties
$properties = $this->getProperties($class);
// gets the translatable properties
$translatableProperties = $this->getTranslatableProperties($properties, $class);
$output->writeln(sprintf('Found %d translatable properties: %s', count($translatableProperties), implode(', ', $translatableProperties)));
$defaultLanguage = 'kr'; // fake language
$em = $this->getContainer()->get('doctrine')->getManager();
$repository = $em->getRepository('Gedmo\\Translatable\\Entity\\Translation');
$items = $em->getRepository($class)->findAll();
$propertyAccessor = PropertyAccess::createPropertyAccessor();
foreach ($items as $item) {
foreach ($translatableProperties as $translatableProperty) {
$value = $propertyAccessor->getValue($item, $translatableProperty);
$repository->translate($item, $translatableProperty, $defaultLanguage, $value);
}
$em->flush();
}
}
private function getProperties($class)
{
$phpDocExtractor = new PhpDocExtractor();
$reflectionExtractor = new ReflectionExtractor();
// array of PropertyListExtractorInterface
$listExtractors = array($reflectionExtractor);
// array of PropertyTypeExtractorInterface
$typeExtractors = array($phpDocExtractor, $reflectionExtractor);
// array of PropertyDescriptionExtractorInterface
$descriptionExtractors = array($phpDocExtractor);
// array of PropertyAccessExtractorInterface
$accessExtractors = array($reflectionExtractor);
$propertyInfo = new PropertyInfoExtractor(
$listExtractors,
$typeExtractors,
$descriptionExtractors,
$accessExtractors
);
return $propertyInfo->getProperties($class);
}
private function getTranslatableProperties($properties, $class)
{
$translatableProperties = [];
// https://gist.github.com/Swop/5990316
$annotationReader = new AnnotationReader();
foreach ($properties as $property) {
try {
$reflectionProperty = new \ReflectionProperty($class, $property);
$propertyAnnotations = $annotationReader->getPropertyAnnotations($reflectionProperty);
foreach ($propertyAnnotations as $propertyAnnotation) {
if ($propertyAnnotation instanceof Translatable) {
// this property is translatable
$translatableProperties[] = $property;
}
}
} catch (\ReflectionException $e) {
// missing property
continue;
}
}
return $translatableProperties;
}
}

accessing repository in symfony

I am using symfony and am inside the controller trying to return a set of records by using a range (rangeUpper, rangeLower).
I am passing the parameters in the request object fine. But when going to route in the controller and trying to access the repository class I am at a loss. My repository looks like;
public function findAllByParams (Request $request)
{
$criteria = $request->query->get('uniflytebundle_material-stock_select');
$criteria = ($request->get('materialId') == 0 ? [] : ['materialId' => $request->get('materialId')]);
$criteria = array_merge(($request->get('gaugeId') == 0 ? [] : ['gaugeId' => $request->get('gaugeId')]), $criteria);
$criteria = array_merge(($request->get('rangeUpper') == 0 ? [] : ['rangeUpper' => $request->get('rangeUpper')]), $criteria);
$criteria = array_merge(($request->get('rangeLower') == 0 ? [] : ['rangeLower' => $request->get('rangeLower')]), $criteria);
$criteria = array_filter($criteria);
$query = $this->createQueryBuilder('ms');
if (!empty($criteria)) {
if (!empty($criteria['materialId']) && !empty($criteria['gaugeId']) && !empty($criteria['rangeUpper']) && !empty($criteria['rangeLower'])) {
$query
->where('ms.material = :materialId')
->andWhere('ms.gauge = :gaugeId')
->andWhere('ms.widthDecimal <= :upperIdentifier')
->andWhere('ms.widthDecimal >= :lowerIdentifier')
->setParameter('materialId', $criteria['materialId'])
->setParameter('gaugeId', $criteria['gaugeId'])
->setParameter('upperIdentifier', $criteria['rangeUpper'])
->setParameter('lowerIdentifier', $criteria['rangeLower'])
;
}
}
$query->orderBy('ms.widthDecimal', 'DESC');
return $query->getQuery()->getResult();
}
My current controller looks like
public function selectStripWidthAction (Request $request)
{
$em = $this->getDoctrine()->getManager();
$materialId = $request->get('materialId');
$gaugeId = $request->get('gaugeId');
$materialStocks = $em->getRepository('UniflyteBundle:MaterialStock')->findAllByParams($request);
return $this->render('materialstock/dialog/select.html.twig', [
'MaterialStocks' => $materialStocks,
]);
}
In the Repository I have the query setup to accept and query by the Range. How do I pass the request object and retrieve the result set from the Repository? FindAllByParams call in the controller is not working.
Undefined method 'findAllByParams'. The method name must start with either findBy or findOneBy!
Thanks in advance for your time and effort.
Check that your MaterialStock have annotation for repository.
<?php
namespace UniflyteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MaterialStock
*
* #ORM\Table(name="material_stock")
* #ORM\Entity(repositoryClass="UniflyteBundle\Repository\MaterialStockRepository")
*/
class MaterialStock{
// [...]
}

Calculate changeSet for object that have proxy entity in property for logging purpose - Doctrine

I have problem with calculating values for change set. I need old and new value for reference entities inside current one. Thing is that those entites are populated with proxy object to avoid loading it from db. Because I already know old value and new value for ID. So code is looking like this :
// in code
$entity->setEntity1($em->getReference(Entity1::class, $id));
...
// event callback for getting diff
protected function postUpdate(LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entity = $args->getEntity();
$classMetadata = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($classMetadata, $entity);
$changeSet = $uow->getEntityChangeSet($entity);
echo json_encode($changeSet);
}
and result is :
{
"entity1": [
{
"__initializer__": {},
"__cloner__": {},
"__isInitialized__": false
},
{
"__initializer__": {},
"__cloner__": {},
"__isInitialized__": false
}
]
}
and desired output would be :
{
"entity1": [ 101, 105]
}
So proxy entity is translated to its property. How can I avoid doing that and have only IDs instead?
Thanks
Solution is really simple one. I was fooled by printed output of json. Object inside that key of changeSet is not simple one. It's whole proxy object so solution is just to call getId of that proxy and replace value inside change set array.
// event callback for getting diff
protected function postUpdate(LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entity = $args->getEntity();
$classMetadata = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($classMetadata, $entity);
$changeSet = $uow->getEntityChangeSet($entity);
if (array_key_exists('entity1', $changeSet) {
if ($changeSet['entity1'][0] != null) {
$changeSet['entity1'][0] = $changeSet['entity1'][0]->getId();
}
if ($changeSet['entity1'][1] != null) {
$changeSet['entity1'][1] = $changeSet['entity1'][1]->getId();
}
}
// do something with it
}
I am hoping that this will help others and waste time as I am.

Validation on extended fields UserDefinedForm

I have made an extension on the UserDefinedForm (module userforms). This works well, but I cannot figure out how to set validation on this extra fields. This is (a part of) my code:
class UserDefinedPaymentForm_Controller extends UserDefinedForm_Controller {
private static $allowed_actions = array(
"finished",
"complete",
"error"
);
public function getFormFields() {
//Payment fields
$supported_methods = PaymentProcessor::get_supported_methods();
$gateways = array();
foreach ($supported_methods as $methodName) {
$methodConfig = PaymentFactory::get_factory_config($methodName);
$gateways[$methodName] = $methodConfig['title'];
}
$fields = parent::getFormFields();
$fields->add(new NumericField("PaymentAmount", _t('UserDefinedPaymentForm.PAYMENT_AMOUNT', 'Payment Amount')));
$fields->add(new Literalfield("literalfield", _t('UserDefinedPaymentForm.PAY', '<h2>Pay</h2>')));
$fields->add(new Literalfield("literalfield", _t('UserDefinedPaymentForm.PAY_INSTRUCTIONS', '<p>Choose your prefered payment method and click Pay:</p>')));
$fields->add(new DropdownField("PaymentMethod", _t('UserDefinedPaymentForm.PAYMENT_METHOD', 'Payment Method'), $gateways));
return $fields;
}
}
Now I want to validate the field PaymentAmount, the value of this field has to be 2 or more. How can I do this?
I would guess (I haven't tested this) your best bet is to create a subclass of UserFormValidator and override the php($data) method.
Then, in your UserDefinedPaymentForm_Controller, you will also need to override the Form method.
class PaymentAmountUserFormValidator extends UserFormValidator {
public function php($data) {
$result = parent::php($data);
if ($result === true) {
// verify your PaymentAmount here and return true or false, accordingly
}
return $result;
}
class UserDefinedPaymentForm_Controller {
...
public function Form()
{
$form = UserForm::create($this);
// Generate required field validator
$requiredNames = $this
->getController()
->Fields()
->filter('Required', true)
->column('Name');
$validator = new PaymentAmountUserFormValidator($requiredNames);
$form->setValidator($validator);
$this->generateConditionalJavascript();
return $form;
}
...
}

doctrine 2 - removing all elements from arraycollection does not invoke any Event

I need to invoke event when arraycollection is cleared (has no elements), pre/postUpdate is not invoked then. Only if I change contents of arraycollection and there is at least one element after change pre/postUpdate events are invoked.
Any Idea how to make it work?
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
if ($entity instanceof Track) {
// remove all old statuses
$query = $em->createQuery(
'SELECT os
FROM CuculoERPBundle:OrderStatus os
WHERE os.track = :track'
)
->setParameter(':track', $entity->getId());
$orderStatusArray = $query->getResult();
foreach ($orderStatusArray as $orderStatus) {
$em->remove($orderStatus);
}
// add new statuses
foreach ($entity->getOrders() as $order) {
var_dump($order->getId());
$orderStatus = new OrderStatus();
$orderStatus->setOrder($order);
$orderStatus->setTrack($entity);
$orderStatus->setStep($em->getReference('Cuculo\ERPBundle\Entity\OrderStep', 6));
$em->persist($orderStatus);
}
$this->needsFlush = true;
}//end if
}//end postUpdate()
I managed to do this by copying code after // remove all old statuses to UpdateAction and adding conditional to count ArrayCollection and if 0 exec this code.

Resources