Symfony3 updates a bit to much - doctrine

I have a CMS hobby project where I'm having problems when running an update in Symfony.
I have three Entities that link to each other.
Article Entity:
/**
* #var \StaticBundle\Entity\Revision
*
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\OneToOne(targetEntity="StaticBundle\Entity\Revision")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="revision", referencedColumnName="rid")
* })
*/
private $revision;
Revision Entity:
/**
* #var \StaticBundle\Entity\Content
*
* #ORM\ManyToOne(targetEntity="StaticBundle\Entity\Content")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="content", referencedColumnName="cid")
* })
*/
private $content;
Content Entity:
This is a plain and simple Entity. No relations here.
Controller method:
/**
* #param Entity $entity $form->getData(); (Can be Article, Page etc)
* #param Person $person The user (FOSUserBundle)
*/
private function newRevision($entity, $person) {
$em = $this->getDoctrine()->getManager();
// Either fetch Content Entity from DB or the $entity.
$content = $em->getRepository('StaticBundle:Content')
->find($entity->getRevision()->getContent()->getCid());
// Creating the new Revision Entity.
$revision = new Revision();
...
$revision->setContent($content);
// instantiating the node type. EX: Article
$contentType = $entity->getRevision()->getContent()->getContenttype();
$contentTypeObject = $contentType->getNamespace() . "\\" . $contentType->getName();
$newEntity = new $contentTypeObject();
$newEntity->setRevision($revision);
// Setting the value from the article form $entity to the new Entity
$object = new \ReflectionObject($newEntity);
foreach ($object->getMethods() as $method) {
if($method->name != "setRevision") {
if(substr($method->name, 0, 3) == "set") {
$set = (string)$method->name;
$get = "get" . substr($method->name, 3);
$newEntity->$set($entity->$get());
}
}
}
$em->flush();
dump($newEntity);
}
The system is suppose to be able to have multiple revisions of the "node".
EX:
Content id: 21 can have
Revision id: 22 (name: b)
Revision id: 23 (name: c)
But when I want to create a new Revision (id: 24 name: d), both the new and the existing Revision is updated with the new name.
So now it looks like this:
Content id: 21 can have
Revision id: 22 (name: b)
Revision id: 23 (name: d)
Revision id: 24 (name: d)
How do I avoid this? The doctrine is done with a simple flush();

Related

yii2-websocket issue for getting online users list

I am using this package for a chat application. I am facing issue to get the online users list. There is a way suggested by someone I tried that but no success.
Code below for getting the online users list.
/**
* Subscribe to messages
*
* #param ConnectionInterface $client
* #param string $msg
*/
public function commandSubscribe(ConnectionInterface $client, $msg)
{
$request = #json_decode($msg, true);
$client->talkId = $request['talk_id'] ?? null;
$client->userId = $request['user_id'] ?? null;
$this->clients = $client;
foreach ($this->clients as $key=>$chatClient) {
$onlineUsers[] = $chatClient->name;
}
$client->send( json_encode(['onlineUsers'=> $onlineUsers, 'room'=>$client->talkId, 'user' =>$client->userId ,'message'=> 'User added to room']) );
}
I get the below response:
Response:{"onlineUsers":{},"room":"provider","user":"hassan","message":"User added to room"}

Symfony3 Doctrine : Fetch entities as arrays with all associations for performance improvement

I have 2 entities : Product and Tag. A product can has 0 or one tag.
Product entity :
/**
* #ORM\Column(type="string", length=25)
* #ORM\Id
*/
private $code;
/**
* #ORM\Column(type="string", length=50)
*/
private $name;
/**
* #ORM\Column(type="string", length=35, nullable=true)
*/
private $tagCode;
/**
* #ORM\ManyToOne(targetEntity="Tag")
* #ORM\JoinColumn(name="tagCode", referencedColumnName="tagCode")
*/
private $tag;
I have a page where I want to list all my products.
I use KnpPaginator bundle to paginate my 1000+ results (50 per page).
ProductController listAction()
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery('SELECT p FROM AppBundle:Product p ORDER BY p.code ASC');
// Pagination
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($query, $request->query->getInt('page', 1), 50);
return $this->render('products/list.html.twig', array(
'products' => $pagination
));
It works well but it's a little bit too time consuming (more than 7 seconds to display 50 first results).
Instead of fetching my products as entities (my entities are quite large with a lot of dependencies and associations), I would like to use arrays (for increasing the page loading speed).
Besides, I want to show just some properties of my entity Product.
product name
product code (= primary key)
tag code (= secondary key)
tag name
I tried to use $query->getArrayResult() but it doesn't return Tag object and I have to get Tag object to show the tag name in twig :
{% for product in products %}
...
<td>{{ product.tag.name }}</td>
...
{% endfor %}
$query->getArrayResult() returns an array of products like this :
array (size=3)
'code' => string 'TEST' (length=4)
'name' => string 'My product' (length=10)
'tagCode' => string 'mytag' (length=5)
My question is : how to get Tag object (or tag name) in my array of results ?

Magento Cart Same product adding in different lines

While adding same product to cart, it's adding in new line but it needs to update the qty, if the same product id already in cart.
I need it in same line with qty update.
You need to override app/code/core/Mage/Sales/Model/Quote/Item.php class. representProduct method is responsible for new row or line. To add same product multiple time you have to return false every time.
I have assumed the you have overried this class successfully.
/**
* Check product representation in item
*
* #param Mage_Catalog_Model_Product $product
* #return bool
*/
public function representProduct($product)
{
return false;
}
Thanks.
You need to hook catalog_product_load_after and add below code in observer.php.
public function addPostData(Varien_Event_Observer $observer) {
$action = Mage::app() -> getFrontController() -> getAction();
if ($action -> getFullActionName() == 'checkout_cart_add') {
if ($action -> getRequest() -> getParam('seller')) {
$item = $observer -> getProduct();
$additionalOptions = array();
/// Add here your additional data
$additionalOptions[] = array('label' => 'Seller', 'value' => "seller 1");
$item -> addCustomOption('additional_options', serialize($additionalOptions));
}
}
}
In case you don't have any custom data to apply, Set some random info like added_at => 4:21 PM, 13 June 2016
Check this link for more info.

Magic Doctrine2 finders when field has underscore?

I'm having problems using find*() magic methods of Doctrine2 when the field has an underscore in between.
$repository->findByName("Hello"); // Works
$repository->findByIsEnabled(true);
Entity 'Acme\SecurityBundle\Entity\Package' has no field 'isEnabled'.
You can therefore not call 'findByIsEnabled' on the entities'
repository.
This is the simple entity definition in YAML for replicating the error:
Acme\SecurityBundle\Entity\Package:
type: entity
repositoryClass: Acme\SecurityBundle\Repository\PackageRepository
table: security_package
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 255
unique: true
is_enabled:
type: boolean
I recall having had the same problem and think I solved it by writing something like this :
$repository->findBy(array('is_enabled' => true));
Let's look at the code :
<?php
/**
* Adds support for magic finders.
*
* #return array|object The found entity/entities.
* #throws BadMethodCallException If the method called is an invalid find* method
* or no find* method at all and therefore an invalid
* method call.
*/
public function __call($method, $arguments)
{
if (substr($method, 0, 6) == 'findBy') {
$by = substr($method, 6, strlen($method));
$method = 'findBy';
} else if (substr($method, 0, 9) == 'findOneBy') {
$by = substr($method, 9, strlen($method));
$method = 'findOneBy';
} else {
throw new \BadMethodCallException(
"Undefined method '$method'. The method name must start with ".
"either findBy or findOneBy!"
);
}
if ( !isset($arguments[0])) {
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
throw ORMException::findByRequiresParameter($method.$by);
}
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
return $this->$method(array($fieldName => $arguments[0]));
} else {
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
}
}
The key line is here:
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
Now let's have a look to classify :
<?php
/**
* Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName'
*
* #param string $word Word to classify
* #return string $word Classified word
*/
public static function classify($word)
{
return str_replace(" ", "", ucwords(strtr($word, "_-", " ")));
}
It looks like you're supposed to write your fields "likeThis" if you want this to work.

How to update record with OneToMany relationship in Doctrine 2?

I have a user entity and I'm trying to update it from a UserService. The problem comes when I try to update a property which is set as an array collection.
/**
*
* #param \Doctring\Common\Collections\Collection $property
* #OneToMany(targetEntity="Countries",mappedBy="user", cascade={"persist", "remove"})
*/
private $countries;
I'm not sure if I'm supposed to somehow delete all the $countries before I set them back or if there's a way to select which ones to delete and set up the different ones....this is what my updateUser method looks so far:
public function updateUser($user) {
$entity = $this->getUser($user['id']);
if (!$entity)
throw new Exception('Error saving user!');
$countries = $this->getCountriesArray($user); //countries already set in the db
$tempCountries = array();
$delete = array();
foreach ($countries as $country) {
$found = false;
if (in_array($country, $user['countries'])) {
$tempCountries[] = $country;
} else {
$delete[] = $country;
}
}
$tempCountries = array_unique(array_merge( //combine the countries from the db we want to leave
$tempCountries, //with those the user selected
$user['countries']));
...
//here I need something to remove the countries in $delete...right?
...
$entity->setEmail($user['email']);
$entity->setResponsable($user['responsable']);
$entity->setPassword($this->createPass($user['password']));
$entity->setUrl($user['url']);
$entity->setRole($user['role']);
$modelCountries = array();
foreach($tempCountries as $c) {
$p = new Countries();
$p->setCountryName($c);
$p->setUser($entity);
$modelCountries[] = $p;
}
$entity->setCountries($modelCountries);
$this->em->persist($entity);
$this->em->flush();
}
please stackOverflow... give me a hand making sense out of this.
It actually depends on your use case.
As you said, you can either delete all countries before to set the new ones, or compute the delta, and update only the needed ones.
What countries are you providing to your service? The delta? Or full list?
Do you have performance constraints? If yes, what is the cost of a DELETE statements vs SELECT then UPDATE?
Compute delta, then UPDATE can be tricky and difficult, in most case, you better want to just DELETE all and INSERT.
For my current and personal choice I am using DQL to DELETE all owing side rows for the mapped entity and then inserting the new one.
class Rule
{
/**
* #OneToMany(targetEntity="Tier", mappedBy="rule", cascade={"persist", "remove"})
* #JoinColumn(name="ruleId", referencedColumnName="ruleId")
* #var Tier[]
*/
public $tiers;
So when I am passing new Tier in my call I am simply Deleting the all Tiers for that Role from the Rule Mapper
public function resetTiers($ruleId)
{
$modelClass = "Tier";
$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();
return $numDeleted;
}
and then calling the usual
$this->doctrineEntityManager->persist($rule);
$this->doctrineEntityManager->flush();
Hope that helps

Resources