How to implement instance counter in symfony? - php

I'm trying to implement an instance counter on a php/symfony3 project, but I don't find how.
I have an "Advert" entity, and I want to show how many instances exist on a Twig view.
So, in the entity class, I created a static attribute private static $nbAdverts = 0; with a getter/setter, and two static methods:
/**
* #ORM\PrePersist
*/
public static function increaseAdverts()
{
self::$nbAdverts++;
}
and
/**
* #ORM\PreRemove
*/
public static function decreaseAdverts()
{
self::$nbAdverts--;
}
I'm calling these methods using the Doctrine events: PrePersist and PreRemove.
These adverts are created in the controller using the Advert repository before being persisted and flushed. Also called using this repository before being removed.
The counter remains stuck at 0 when I add or remove an advert.
I think I'm doing something wrong: is it possible to do this without rewriting the repository add method? If it is, any idea about how?

The behavior you are getting is absolutely normal. You are running a web application, any variable not stored in the session, is set to it default value if exists, at every request. So to achieve what you want to do you can save your static variable in session and update it in the increaseAdverts() and decreaseAdverts() Methods.

Related

Doctrine relationship with non-entity model

I'm trying to create an application where I have two related models: "User" and "Debtor".
User is a doctrine entity that is persisted in my database.
Debtor is a model that is stored on an external system and accessed through an API. I have already created a repository for accessing Debtors with find(), findAll() and findByUserId().
If "Debtor" was a Doctrine Entity, i would have a #OneToMany relationship on the User model in order to access debtors. Something like this:
/**
* #Entity
*/
class User {
/**
* #OneToMany
*/
private $debtors;
public function getDebtors() {
return $this->debtors;
}
public function setDebtors($debtors) {
$this->debtors = $debtors;
return $this;
}
}
One solution would be to call DebtorRepository::findByUserId($this->id) in the getDebtors() method, but I don't think that is the right way to go.
I also thought about have a #PostLoad method that loads the users debtors into the $debtors property.
I'm not much for having that much logic inside my models, but i don't know what other choices I have?
Furthermore, I also need to be able to access various other models through the Debtor model and so i have the issue again.
If resource for your API, I think you should have service which will take care of loading that (as probably you need to check for errors/authentication/...). So I would load it on demand from service.
But if you really want to have it accessible in the entity, then you can use Entity Listener, hook to the postLoad method and inject it there. In this case your entity will still stay thin, because this heavy logic will be in external service.

symfony #oneToMany relation returns only last object in collection

I have a situation and not really sure what I'm doing wrong. in Sumfony 3.3 I've created a relation between entity Page and Language, where Page is related to multiple Languages, and when I search for a Page and get Page object but property Languages returns collection with only last Language object. No matter how many objects are there in collection it always returns last.
Page Entity:
/**
* #ORM\OneToMany(targetEntity="Language", mappedBy="page", cascade={"ALL"}, indexBy="page_id")
*/
private $languages;
Language entity:
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="languages")
*/
private $page;
public function addLanguage(\AppBundle\Entity\Langaugee $language)
{
$this->languages[] = $language;
return $this;
}
public function removeLanguage(\AppBundle\Entity\Language $language)
{
$this->$languages->removeElement($language);
}
public function getLanguages()
{
return $this->languages;
}
Page object is fetching in PageService:
public function getPageByName($name)
{
return $this->pageRepository->findBy(array("name"=>$name));
}
Since property $languages by default set on lazy, JMS serializer when serializes Page object it's fetching languages collection
Did anyone had this problem?
After thorough debugging, I figure it out that indexBy is misused here. Defined indexBy = page_id provided always the same value so every record that is mapped to entity in SimpleObjectHydrator overrun the existing record, leaving only last added Language object in collection
I know Its an old post, but solution below worked for me.
I had to clear EntityManager cache in my repository class method by calling this method just before fetching data.
$this->_em->clear();
here _em is the default EntityManager available in the repository class.

Is it considered a bad practice to add fields to Symfony entity in controller?

Is it considered a bad practice to add fields to Symfony entity in controller? For example lets say that I have a simple entity:
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
}
And then in UserController.php I want to do the following:
foreach($users as $user){
$user->postsCount = someMethodThatWillCountPosts();
}
So later that postsCount can be displayed in Twig. Is it a bad practice?
Edit:
It's important to count posts on side of mysql database, there will be more than 50.000 elements to count for each user.
Edit2:
Please take a note that this questions is not about some particular problem but rather about good and bad practices in object oriented programming in Symfony.
As #Rooneyl explained that if you have relation between user and post then you can get count easily in your controller, refer this for the same. But if you are looking to constructing and using more complex queries from inside a controller. In order to isolate, reuse and test these queries, it's a good practice to create a custom repository class for your entity.Methods containing your query logic can then be stored in this class.
To do this, add the repository class name to your entity's mapping definition:
// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
*/
class Product
{
//...
}
Doctrine can generate empty repository classes for all the entities in your application via the same command used earlier to generate the missing getter and setter methods:
$ php bin/console doctrine:generate:entities AppBundle
If you opt to create the repository classes yourself, they must extend
Doctrine\ORM\EntityRepository.
More Deatils
Updated Answer
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. where posts can be commented, you always have to assume that a post draws hundreds of comments. In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This can lead to pretty serious performance problems, if your associations contain several hundreds or thousands of entities.
With Doctrine 2.1 a feature called Extra Lazy is introduced for associations. Associations are marked as Lazy by default, which means the whole collection object for an association is populated the first time its accessed. If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection: SOURCE
"rather about good and bad practices in object oriented programming"
If that's the case then you really shouldn't have any business logic in controller, you should move this to services.
So if you need to do something with entities before passing them to twig template you might want to do that in specific service or have a custom repository class that does that (maybe using some other service class) before returning the results.
i.e. then your controller's action could look more like that:
public function someAction()
{
//using custom repository
$users = $this->usersRepo->getWithPostCount()
//or using some other service
//$users = $this->usersFormatter->getWithPostCount(x)
return $this->render('SomeBundle:Default:index.html.twig', [
users => $users
]);
}
It's really up to you how you're going to do it, the main point to take here is that best practices rather discourage from having any biz logic in controller. Just imagine you'll need to do the same thing in another controller, or yet some other service. If you don't encapsulate it in it's own service then you'll need to write it every single time.
btw. have a read there:
http://symfony.com/doc/current/best_practices/index.html

Symfony 2 - FOSUserBundle - Create and Persist another entity inside the constructor of User class

I'm making a website where users can create albums.
I would like to create a default album per user.
I would like to create an Album entity and to persist it in the constructor of my User class.
Is it possible ?
I just know that the entityManager is not accessible from an Entity... That's why it's a problem for me.
Even though this technically IS possible I would strongly recommend you not to do this.
To answer your question, it is possible and it would be done like this:
class User extends FOSUser
{
/**
* #ORM\OneToMany(targetEntity="Album", cascade={"persist"})
*/
private $albums;
public function __construct()
{
$this->albums = new ArrayCollection();
$this->addAlbum(new Album());
}
public function addAlbum(Album $album)
{
$this->albums[] = $album;
}
public function getAlbums()
{
return $this->albums:
}
}
With setup like this whenever you create a new user and save it, a related album will be created together with it. I have to repeat, even though it's possible, don't do it like this.
Good solutions
There are few strategies that can be used to achieve what you want.
FOSUserBundle master
If you're not using 1.3.x version of FOSUserBundle but master, you can see that RegistrationController fires a few events. The one you're interested in is FOSUserEvents::REGISTRATION_INITIALIZE. You should create an event listener and add album to user in your listener.
FOSUserBundle 1.3.x
If you're using one of older versions, these events don't exist unfortunately and you can do it two ways.
Extend FOSUserBundle UserManager and override createUser method. You can add your album adding logic there. I would prefer this approach.
Override FOSUserBundle RegistrationController::registerAction. It can be viable option sometimes but in your case I think option 1 is better.

How to override sonataNotificationBundle's Sonata\NotificationBundle\Iterator\MessageManagerMessageIterator class?

Sonata Notification Bundles backend command waits for number of iterations passed to command to be completed , I want the backend command to just iterate the number of times equal to messages available. It should send all messages and exit.
So I want to override Sonata\NotificationBundle\Iterator\MessageManagerMessageIterator class.
I copied it to Application\Sonata\NotificationBundle\Iterator\MessageManagerMessageIterator , Application\Sonata is a child bundle which is generated by Sonata at the time of installation. But still it is pointing to original parent bundles class and not using this class.
The iterator is used in,
Sonata\NotificationBundle\Backend\MessageManagerBackend.php class in below method.
/**
* {#inheritdoc}
*/
public function getIterator()
{
$types = null !== $this->type ? array($this->type) : array();
return new MessageManagerMessageIterator($this->messageManager, $types, $this->pause, $this->batchSize);
}
How do I override MessageManagerMessageIterator ?
The simple way is to use bundle inheritance feature. It is not very clean solution, but still it will work.

Categories