Doctrine oneToMany - querying for one - symfony

I have a one to many relationship where one page has many versions.
**page**
id
parent_id
**page_version**
id
page_id
title
published
date
These are related by the foreign key page_id. So I have a Page entity and a PageVersion entity:
class Page
{
/**
* #ORM\OneToMany(targetEntity="PageVersion", mappedBy="page")
*/
private $pageversions;
}
class PageVersion
{
/**
* #var page
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="pageversions")
*
*/
private $page;
}
So I can get all page versions for each page. However, each page will only ever have one published page version. Therefore, when querying how can I get the latest version? I.e Where published = '1' rather than getting a list of all versions?
Here is an example of my query:
public function findAllPages()
{
$query = $this->getEntityManager()
->createQuery('
SELECT DISTINCT p,v
FROM XYZWebsiteBundle:Page p
JOIN p.pageversions v
WHERE v.published = :published
AND v.deleted = :deleted'
)->setParameters(array('published' => 1, 'deleted' => 0));
try {
return $query->getResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
However if I loop through the result I can only access the Page properties and not PageVersion properties e.g title, published, date etc.
$entities = $em->getRepository('XYZWebsiteBundle:Page')->findAllPages();
foreach($entities as $entity){
print($entity->getPageVersion()->getTitle());
}
The error returned is Fatal error: Call to undefined method Doctrine\ORM\PersistentCollection::getTitle().

$er = $this->getDoctrine()->getRepository('XYZWebsiteBundle:Page');
$qb = $er->createQueryBuilder('p')
->where('p.published = :published')
->setParameter('published', 1)
;
$entities=$qb->getQuery()->getResult();
If you are sure there will never be no more than 1 page with the published status, you can even use getOneOrNullResult() instead of getResult(). This returns null if there is no matching entity, the entity if there is one matching, and an exception if there is more than one.
Edit: ok, so you have to build the query on PageVersion rather than page then. Try:
$er = $this->getDoctrine()->getRepository('XYZWebsiteBundle:PageVersion');
$qb = $er->createQueryBuilder('p')
->where('p.page = :page')
->setParameter('page', $page)
->andWhere('p.published = :published')
->setParameter('published', 1)
;
$pageVersion=$qb->getQuery()->getResult();
with $page being the page you are trying to find the published version for.

Related

Is there a way to define first item of pagination results?

I'm setting up a Symfony project displaying paginated blogposts with an admin interface to manage all this stuff. On this admin it is also possible to "highlight" one of those public blogposts so that this highlighted one is displayed at first position only on the first page.
I need the same item count on each page and that is the problem I'm dealing with.
I'm using PagerFanta so I created an AbstractRepository with a "paginate" function.
protected function paginate(QueryBuilder $qb, $limit = 20, $offset = 0)
{
if ($limit == 0) {
throw new \LogicException('$limit must be greater than 0.');
}
//Instantiates the pagination object with the result of the query
$pager = new Pagerfanta(new DoctrineORMAdapter($qb));
//Sets max data per page
$pager->setMaxPerPage($limit);
//Sets the current page
$pager->setCurrentPage($offset);
return $pager;
}
In my blogpost repository I made a querybuilder to get all public blogpost excluding the highlighted one because I can get it in another way to display it on top of the first page.
public function findAllVisible($id, $limit = 3, $offset = 1, $order = 'DESC')
{
$qb = $this
->createQueryBuilder('a')
->select('a')
->where('a.website = :website')
->setParameter('website', 'blog')
->andWhere('a.public = :public')
->setParameter('public', true)
->andWhere('a.id != :id')
->setParameter('id', $id)
->orderBy('a.dateInsert', $order)
;
return $this->paginate($qb, $limit, $offset);
}
So I first tried to change the limit and the offset according to the current page but I logically lost one item between the first and the second page.
Then I tried to include the highlighted blogpost in querybuilder but I don't know how to define it as the first result if the current page is the first one.
Any idea of how to force the first result to be the highlighted blogpost only on first page? Or another clean and appropriate way to display results as expected?
I answer to myself because I managed to do what I needed to. In case of someone is dealing with the same issue, here is how I did.
I don't use PagerFanta anymore but Doctrine Paginator tool.
Instead of excluding my highlighted article from my query I replaced my initial ORDER BY by a.id = :highlightedId DESC, a.dateInsert DESC.
Now it's working as expected.
Here is my new repository function:
/**
* Finds all visible articles
*
* #param int $highlightedTipId the highlighted tip id
* #param int $page current page
* #param int $limit max items per page
*
* #throws InvalidArgumentException
* #throws NotFoundHttpException
*
* #return Paginator
*/
public function findAllVisible($highlightedTipId, $limit = 3, $page)
{
if (!is_numeric($page)) {
throw new InvalidArgumentException('$page value is incorrect (' . $page . ').');
}
if ($page < 1) {
throw new NotFoundHttpException('Page not found');
}
if (!is_numeric($limit)) {
throw new InvalidArgumentException('$limit value is incorrect (' . $limit . ').');
}
$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery(
"SELECT
a,
CASE WHEN a.id = :id THEN 1 ELSE 0 END AS HIDDEN sortCondition
FROM App\Entity\Item a
WHERE
a INSTANCE OF App\Entity\TipArticle
AND
a.website = :website
AND
a.public = :public
ORDER BY
sortCondition DESC,
a.dateInsert DESC
"
);
$query->setParameter(':website', 'blog');
$query->setParameter(':public', true);
$query->setParameter(':id', $highlightedTipId);
$firstResult = ($page - 1) * $limit;
$query
->setFirstResult($firstResult)
->setMaxResults($limit);
$paginator = new Paginator($query);
if (($paginator->count() <= $firstResult) && $page != 1) {
throw new NotFoundHttpException('Page not found');
}
return $paginator;
}
A word about this line CASE WHEN a.id = :id THEN 1 ELSE 0 END AS HIDDEN sortCondition: it is the only way I found to do a ORDER BY a.id = :highlightedId DESC with Doctrine. As you can see I made a DQL but I is also possible with QueryBuilder.
Hope it will help! :)
Nice, well done. If I may offer some advice though. In a repo you shouldn't need to get the ObjectManager ($this->getEntityManager()) as the repo is already for a type of Entity, Item in this case. You should use Criteria instead. practical example docs
You should have the ObjectManager in whichever controller you've got, so then you'd do:
$items = $this-getObjectManager()->getRepository(Item::class)->findAllVisible($params)
For use of the Paginator you should use the QueryBuilder, with Expressions, like here
As a practical example, an indexAction of mine:
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator as OrmPaginator;
use DoctrineORMModule\Paginator\Adapter\DoctrinePaginator as OrmAdapter;
use Zend\Paginator\Paginator;
public function indexAction()
{
// get current page, defaults to 1
$page = $this->params()->fromQuery('page', 1);
// get current page size, defaults to 10
$pageSize = $this->params()->fromQuery('pageSize', 10);
// get ordering, defaults to 'createdAt'
$orderBy = $this->params()->fromQuery('orderBy', 'createdAt');
// get order direction, defaults to Criteria::DESC
$orderDirection = ($this->params()->fromQuery('orderDirection') === Criteria::ASC)
? Criteria::ASC
: Criteria::DESC;
$criteria = new Criteria();
$criteria->setFirstResult($page * $pageSize);
$criteria->setMaxResults($pageSize);
$criteria->orderBy([$orderBy => $orderDirection]);
/** #var QueryBuilder $queryBuilder */
$queryBuilder = $this->getObjectManager()->createQueryBuilder();
$queryBuilder->select('a')->from(Article::class, 'a');
$queryBuilder->addCriteria($criteria);
$paginator = new Paginator(new OrmAdapter(new OrmPaginator($queryBuilder)));
$paginator->setCurrentPageNumber($page); // set current page
$paginator->setItemCountPerPage($pageSize); // set item count per page
return [
'paginator' => $paginator,
'queryParams' => $this->params()->fromQuery(), // pass these for your pagination uri's
];
}
NOTE: In the above the $this is an instance of a Zend Framework controller, where ->fromQuery returns (if present) a given key from the Query bit if a URI, else return the 2nd param default (or null). You should do something similar.

Add distinct values to array collection

Within my application I am sending out notifications to different users, which are assigned to agencies, which are assigned to documents. So whenever a document gets created and an agency is assigned to that document, all the users belonging to that agency should get a notification. The problem: it may happen, that an user is assigned to multiple agencies so whenever the notifications get sent out and all his agencies are assigned to the document, he would get notified multiple times. I'd like to avoid this but I can't figure out how to add only distinct objects to my array collection since it doesn't seem like there's something like the array_unique function.
So far it looks like that:
foreach($document->getAgencies() as $agency) {
if(count($agency->getUseragencies()) > 0){
$users = $agency->getUseragencies();
foreach ($users as $user){
... notifications are generated
$manager->addNotification($user, $notif);
}
}
}
Any help would be appreciated!
oh and background info: Agency is an own entity, as well as User is and they are in a many to many relationship!
edit
mapping infos:
in entity Agency:
/**
* #ORM\ManyToMany(targetEntity="UserBundle\Entity\User", mappedBy="agencies")
**/
private $useragencies;
in entity User
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Agency", inversedBy="useragencies", cascade={"persist"})
* #ORM\JoinTable(name="user_user_agencies",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="iata8", referencedColumnName="iata8")})
* #var \AppBundle\Entity\Agency
**/
private $agencies;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Notification", mappedBy="user", orphanRemoval=true, cascade={"persist"})
**/
private $notifications;
ArrayCollection has a method contains that you could use - link.
I assume your Agency entity has also bidirectional mapping with Document entity i guess as ManyToMany. To get the users whom you want to send the notification which belongs to multiple agencies and an agency can have many documents you can use below DQL to get distinct users by providing the document id if you have any other property to identify the document you can adjust WHERE clause accordingly
SELECT DISTINCT u
FROM YourBundle:User u
JOIN u.agencies a
JOIN a.documents d
WHERE d.id = :documentid
Using query builder it would be something like
$user = $em->getRepository('YourBundle:User');
$user = $user->createQueryBuilder('u')
->select('u')
->join('u.agencies','a')
->join('a.documents','d')
->where('d.id = :documentid')
->setParameter('documentid', $documentid)
->distinct()
->getQuery();
$users= $user->getResult();
While you cannot use in_array() on an ArrayCollection, you'll have to build your own array of unique users.
$users = array();
foreach($document->getAgencies() as $agency) {
if(count($agency->getUseragencies()) > 0) {
foreach ($agency->getUseragencies()as $user) {
// ... notifications are generated
if(!in_array($user->getId(), $users)) {
$users[] = $user->getId();
}
}
foreach($users as $userId) {
$manager->addNotification($userId, $notif);
}
}
}
or a simpler, lower-cost version:
$sent = array();
foreach($document->getAgencies() as $agency) {
if(count($agency->getUseragencies()) > 0) {
foreach ($agency->getUseragencies()as $user) {
// ... notifications are generated
if(!in_array($user->getId(), $sent)) { // check if user has already been sent the notification
$manager->addNotification($user, $notif); // send the notification
$sent[] = $user->getId(); // add user to the 'sent' list
}
}
}
}
Alternatively, you could save yourself a lot of trouble by writing a custom DQL Query (possibly in your UserRepository class) to fetch the list of user from database directly. This would remove a lot of complexity in the code by removing the need for a loop altogether.
You need to add a condition to handle values already in the array. Try adding something like the condition below.
foreach($user as $user){
if(!in_array($value, $list, true))

Update a field after Linking / Unlinking Many-Many records in SilverStripe

I have created a Customer DataObject by extending Member. Customer has a many_many data relation with a Package DataObject.
I would like increment/decrement a Credits field in the Customer DataObject when a Package is linked / unlinked through the CMS based on the Limit field in the Package table.
Customer
class Customer extends Member {
private static $db = array(
'Gender' => 'Varchar(2)',
'DateOfBirth' => 'Date',
'Featured' => 'Boolean',
'Credits' => 'Int'
);
private static $many_many = array(
'Packages' => 'Package'
);
public function getCMSFields() {
$fields = new FieldList();
$config = GridFieldConfig_RelationEditor::create();
$config->removeComponentsByType('GridFieldAddNewButton');
$packageField = new GridField(
'Packages',
'Package',
$this->Packages(),
$config
);
$fields->addFieldToTab('Root.Package', $packageField);
Session::set('SingleID', $this->ID);
$this->extend('updateCMSFields', $fields);
return $fields;
}
}
Package
class Package extends DataObject {
private static $db = array(
'Title' => 'Varchar(255)',
'Limit' => 'Int'
);
private static $belongs_many_many = array(
'Customers' => 'Customer'
);
}
When you create or delete many to many relationship just one record is modified in your database - the one in table which joins elements of both sides of the relationship. Therefore neither object the relationship is based on is updated. This is why methods like: onBeforeWrite, onAfterWrite, onBeforeDelete and onAfterDelete will not be called at all and you cannot use them to detect such change.
However, Silverstripe provides class ManyManyList which is responsible for all operations connected to many to many relationships. There are two methods which are of your interest: add and remove. You can override them and put inside action to do what you need. These methods are obviously called on each link or unlink operation no matter object types are, so you should make some filtering on classes you are particularly interested in.
The proper way to override the ManyManyList class is to use Injector mechanism, so as not to modify anything inside the framework or cms folder. The example below uses relationship between Members and Groups in Silverstripe but you can easily adopt it to your need (Customer -> Member; Package -> Group).
app.yml
Injector:
ManyManyList:
class: ManyManyListExtended
ManyManyListExtended.php
/**
* When adding or removing elements on a many to many relationship
* neither side of the relationship is updated (written or deleted).
* SilverStripe does not provide any built-in actions to get information
* that such event occurs. This is why this class is created.
*
* When it is uses together with SilverStripe Injector mechanism it can provide
* additional actions to run on many-to-many relations (see: class ManyManyList).
*/
class ManyManyListExtended extends ManyManyList {
/**
* Overwritten method for adding new element to many-to-many relationship.
*
* This is called for all many-to-many relationships combinations.
* 'joinTable' field is used to make actions on specific relation only.
*
* #param mixed $item
* #param null $extraFields
* #throws Exception
*/
public function add($item, $extraFields = null) {
parent::add($item, $extraFields);
if ($this->isGroupMembershipChange()) {
$memberID = $this->getMembershipID($item, 'MemberID');
$groupID = $this->getMembershipID($item, 'GroupID');
SS_Log::log("Member ($memberID) added to Group ($groupID)", SS_Log::INFO);
// ... put some additional actions here
}
}
/**
* Overwritten method for removing item from many-to-many relationship.
*
* This is called for all many-to-many relationships combinations.
* 'joinTable' field is used to make actions on specific relation only.
*
* #param DataObject $item
*/
public function remove($item) {
parent::remove($item);
if ($this->isGroupMembershipChange()) {
$memberID = $this->getMembershipID($item, 'MemberID');
$groupID = $this->getMembershipID($item, 'GroupID');
SS_Log::log("Member ($memberID) removed from Group ($groupID)", SS_Log::INFO);
// ... put some additional actions here
}
}
/**
* Check if relationship is of Group-Member type.
*
* #return bool
*/
private function isGroupMembershipChange() {
return $this->getJoinTable() === 'Group_Members';
}
/**
* Get the actual ID for many-to-many relationship part - local or foreign key value.
*
* This works both ways: make action on a Member being element of a Group OR
* make action on a Group being part of a Member.
*
* #param DataObject|int $item
* #param string $keyName
* #return bool|null
*/
private function getMembershipID($item, $keyName) {
if ($this->getLocalKey() === $keyName)
return is_object($item) ? $item->ID : $item;
if ($this->getForeignKey() === $keyName)
return $this->getForeignID();
return false;
}
}
The solution provided by 3dgoo should also work fine but IMO that code does much more "hacking" and that's why it is much less maintainable. It demands more modifications (in both classes) and needs to be multiplied if you would like to do any additional link/unlink managing, like adding custom admin module or some forms.
The problem is when adding or removing items on a many to many relationship neither side of the relationship is written. Therefore onAfterWrite and onBeforeWrite is not called on either object.
I've come across this problem before. The solution I used isn't great but it was the only thing that worked for me.
What we can do is set an ID list of Packages to a session variable when getCMSFields is called. Then when an item is added or removed on the grid field we refresh the CMS panel to call getCMSFields again. We then retrieve the previous list and compare it to the current list. If the lists are different we can do something.
Customer
class Customer extends Member {
// ...
public function getCMSFields() {
// Some JavaScript to reload the panel each time a package is added or removed
Requirements::javascript('/mysite/javascript/cms-customer.js');
// This is the code block that saves the package id list and checks if any changes have been made
if ($this->ID) {
if (Session::get($this->ID . 'CustomerPackages')) {
$initialCustomerPackages = json_decode(Session::get($this->ID . 'CustomerPackages'), true);
$currentCustomerPackages = $this->Packages()->getIDList();
// Check if the package list has changed
if($initialCustomerPackages != $currentCustomerPackages) {
// In here is where you put your code to do what you need
}
}
Session::set($this->ID . 'CustomerPackages', json_encode($this->Packages()->getIDList()));
}
$fields = parent::getCMSFields();
$config = GridFieldConfig_RelationEditor::create();
$config->removeComponentsByType('GridFieldAddNewButton');
$packageField = GridField::create(
'Packages',
'Package',
$this->Packages(),
$config
);
// This class needs to be added so our javascript gets called
$packageField->addExtraClass('refresh-on-reload');
$fields->addFieldToTab('Root.Package', $packageField);
Session::set('SingleID', $this->ID);
$this->extend('updateCMSFields', $fields);
return $fields;
}
}
The if ($this->ID) { ... } code block is where all our session code happens. Also note we add a class to our grid field so our JavaScript refresh works $packageField->addExtraClass('refresh-on-reload');
As mentioned before, we need to add some JavaScript to reload the panel each time a package is added or removed from the list.
cms-customer.js
(function($) {
$.entwine('ss', function($){
$('.ss-gridfield.refresh-on-reload').entwine({
reload: function(e) {
this._super(e);
$('.cms-content').addClass('loading');
$('.cms-container').loadPanel(location.href, null, null, true);
}
});
});
})(jQuery);
Inside the if($initialCustomerPackages != $currentCustomerPackages) { ... } code block there are a number of things you can do.
You could use $this->Packages() to fetch all the current packages associated to this customer.
You could call array_diff and array_merge to get just the packages that have been added and removed:
$changedPackageIDs = array_merge(array_diff($initialCustomerPackages, $currentCustomerPackages), array_diff($currentCustomerPackages, $initialCustomerPackages));
$changedPackages = Package::get()->byIDs($changedPackageIDs);
The above code will add this functionality to the Customer side of the relationship. If you also want to manage the many to many relationship on the Package side of the relationship you will need to add similar code to the Package getCMSFields function.
Hopefully someone can come up with a nicer solution. If not, I hope this works for you.
note: Didn't actually check does the model work but by visually checking this should help you:
On the link you provided you are using
$customer = Customer::get()->Filter...
That returns a DataList of objects, not a singular object unless you specify what is the object you want from the DataList.
If you are filtering the Customers then you want to get a SPECIFIC customer from the DataList, e.g. the first one in this case.
$customer = Customer::get()->filter(array('ID' => $this->CustomerID))->first();
But You should be able to get the singular DataObject with:
$customer = $this->Customer();
As you are defining the Customer as "has_one". If the relation was a Has many, using () would get you a DataList of objects.
Protip:
You don't need to write our own debug files in SilverStripe. It has own functions for it. For example Debug::log("yay"); what writes the output to a file and Debug::dump("yay") that dumps it directly out.
Tip is that you can check what is the object that you accessing right. Debug::dump(get_class($customer)); would output only the class of the object.

Doctrine2 - Sub query join without a relationship

Ok so here's the issue.
I have a Entity named HelpDocuments and an Entity named LogEntry.
HelpDocuments can be dismissed by the user. When this happens I create a LogEntry with the following attributes:
event - eg: helpDocument.dismiss
entity_id - eg: 11
entityDiscriminator - eg: HelpDocument
There are no relationships created between HelpDocument and LogEntry as I'm implementing my own discriminator logic.
So what I'm trying to achieve is query for all HelpDocuments that have not been dismissed. I can do that with sql, left outer subquery join like so:
SELECT HelpDocument.*, temp.*
FROM HelpDocument
LEFT OUTER JOIN(
SELECT LogEntry.entity_id
FROM LogEntry
WHERE LogEntry.entityDiscriminator = 'HelpDocument'
AND LogEntry.event = 'helpDocument.dismiss'
AND LogEntry.entity_id = 11
) as temp ON HelpDocument.id = temp.entity_id
WHERE temp.entity_id IS NULL;
My issue is how do I turn this into DQL given that there is no relationship defined?
Updated Solution:
So the solution was to not use an LEFT OUTER JOIN because they don't exist / make sense in Doctrine2. In the end I had to do a subquery join:
/**
* Filter by User Dismissed
*
* #param $qb
* #param $route
* #return mixed
*/
public function filterQueryByUserDismissed(QueryBuilder $qb, $args)
{
$args = array_merge(array(
"user" => null,
"dismissed" => false
), $args);
/** #var $dismissedQB QueryBuilder */
$dismissedQB = $this->_em->createQueryBuilder();
/*
This line is important. We select an alternative attribute rather than
letting Doctrine select le.id
*/
$dismissedQB->select('le.entityId')
->from('\Mlf\AppBundle\Entity\UserEntityEventLog', 'le')
->where('le.entityDiscriminator = :entityDiscriminator')
->andWhere('le.event = :event')
->andWhere('le.user = :userId');
$function = (true === $args['dismissed']) ? "in" : "notIn";
$expr = $qb->expr()->$function($this->classAlias.'.id', $dismissedQB->getDQL());
/** #var $qb QueryBuilder */
$qb->andWhere($expr)
->setParameter("entityDiscriminator", HelpDocument::getDiscriminator())
->setParameter("event", HelpDocumentEvents::HELPDOCUMENT_DISMISS)
->setParameter("userId", $args["user"]);
// exit($result = $qb->getQuery()->getSQL());
return $qb;
}
This DQL query results in the following SQL:
SELECT h0_.id AS id0
FROM HelpDocument h0_
WHERE (
h0_.id NOT IN (
SELECT l1_.entity_id
FROM LogEntry l1_
WHERE l1_.entityDiscriminator = 'helpDocument'
AND l1_.event = 'helpDocument.dismiss'
AND l1_.user_id = 1
)
)
Yay!
I saw your solution and I have a minor change that will be a huge performance improvement. Especially if you have more then a couple of thousand rows.
public function filterQueryByUserDismissed(QueryBuilder $qb, $args)
{
$args = array_merge(array(
"user" => null,
"dismissed" => false
), $args);
/** #var $dismissedQB QueryBuilder */
$dismissedQB = $this->_em->createQueryBuilder();
/*
This line is important. We select an alternative attribute rather than
letting Doctrine select le.id
*/
$dismissedQB->select('le.entityId')
->from('\Mlf\AppBundle\Entity\UserEntityEventLog', 'le')
->where('le.entityDiscriminator = :entityDiscriminator')
->andWhere('le.event = :event')
->andWhere('le.user = :userId');
// ---- My changes below
// Get an array with the ids
$dismissedIdsMap = $dismissedQB->getQuery()->getResults();
$dismissedIds = array_map(
function($a){
return $a['entityId'];
},
$dismissedIdsMap);
$function = (true === $args['dismissed']) ? "in" : "notIn";
$expr = $qb->expr()->$function($this->classAlias.'.id', $dismissedIds);
// ---- My changes above
/** #var $qb QueryBuilder */
$qb->andWhere($expr)
->setParameter("entityDiscriminator", HelpDocument::getDiscriminator())
->setParameter("event", HelpDocumentEvents::HELPDOCUMENT_DISMISS)
->setParameter("userId", $args["user"]);
// exit($result = $qb->getQuery()->getSQL());
return $qb;
}
The code above is using two queries. If you are using one query, MySQL will create a temporary view from your subquery and then query the view with master query. It is a lot of overhead creating this view. With two queries you will be keeping the "view" in the PHP memory and this will reduce the overhead dramatically.

How to execute two repository functions in one Action

I have a problem. I want to execute this
$events1 = $em->getRepository('AfishaBundle:Event')->getEvents($this->getCurrentRegion(),'2012-11-12');
//$event_repo->clear();`
$events2 = $em->getRepository('AfishaBundle:Event')->getEvents($this->getCurrentRegion(),'2012-11-13');
foreach($events2 as $event){
die(var_dump($event->getSeanses()->toArray()));
}
But result return with first date = "2012-11-12"
when I not use $event_repo->clear();
That's ok but I have a problem with Twig rendering.
Can I execute this without clear() method ? If I can't, please comment.
For sake of completeness, this is getEvents() code:
public function getEvents($region, $date){
$qb = $this->_em->createQueryBuilder();
$qb
->select('e,s')
->from('AfishaBundle:Event', 'e')
->leftJoin('e.seanses', 's')
->andWhere('s.date = :date')
->andWhere('s.region = :region')
->setParameter('region', $region)
->setParameter('date',$date)
;
return $qb->getQuery()->getResult();
}
This is the relation between Event and EventSeans:
/**
* #ORM\OneToMany(
* targetEntity="EventSeans",
* cascade={"persist", "remove", "merge"},
* mappedBy="event"
* )
* #ORM\OrderBy({"time"= "ASC"})
*/
protected $seanses;
Finally, this is date field in EventSeans entity
/**
* #ORM\Column(name="date", type="date")
*/
protected $date;
Please, consider that it's really hard to understand what you are asking for! I understood that when you pass two different dates to the getEvents() function you always get the same result.
It seems that the code you currently posted is ok, but you are going to do is not to select the Event entities with the getEvents() function.
If you ready the query from you QueryBuilder, you are selecting the EventSeans entities such that the $region and $date are the one provided by you! But you are nor selecting the Event entities nor involving the relationship between Event and EventSeans entities.
Maybe with more details and a better explanation of your problem one could helps.
EDIT
I think you should not select both Event and EventSeanses, since you can get all the EventSeanses objects from the Event entity once you retrieve the Event from repo. But I suggest to select all the EventSeanses such that the criteria are satisfied, and then to get the associated Event object directly from the EventSeanses object. Clean and easy.
public function getEventSeanses($region, $date){
$qb = $this->_em->createQueryBuilder();
$qb
->select('s')
->from('AfishaBundle:EventSeanses', 's')
->andWhere("s.date = ':date'")
->andWhere('s.region = :region')
->setParameter('region', $region)
->setParameter('date',$date)
;
return $qb->getQuery()->getResult();
}
Notice that in SQL you should put strings (like a date) like follows 'date' and not simply date.
Then, in your controller you should do as follows:
$events = array();
$region=...
$date=....//year-month-day
$eventseansesList = $em->getRepository('AfishaBundle:EventSeanses')->getEventSeanses($region,$date);
foreach($eventseansesList as $es){
$events[] $es->getEvent(); // this function retrieve the Event associated to the EventSeanses
}
PS: You order $seanses by "time" in Event and not by "date", That's strange!

Resources