unique slugs with multi entities in symfony2 - symfony

I have a small symfony2 application where a user can create pages. Each page should be accessible via the route /{user_slug}/{page_slug}. I have the entities user and page and I use the sluggable behavior for both entities. In order to find the correct page the combination of user_slug and page_slug has to be unique.
What is the best way to check that the combination of user_slug and page_slug is uniqe?

Try this in your prepository:
public function findByUsernameAndSlug($username, $slug)
{
$em = $this->getEntityManager();
$query = $em->createQuery("
SELECT g
FROM Acme\PagesBundle\Entity\Page p
JOIN p.owner u
WHERE u.username = :username
AND p.slug = :slug
")
->setParameter('username', $username)
->setParameter('slug', $slug);
foreach ($query->getResult() as $goal) {
return $goal;
}
return null;
}

Before you persist the entity in your service-layer check whether given combination of user and page slug are unique, if not modify the page slug (append -2 or something like that) or throw an exception:
public function persistPage(Page $page) {
$userSlug = $page->getUser()->getSlug();
$pageSlug = $page->getSlug();
if ($this->pagesRepository->findOneBySlugs($userSlug, $pageSlug) != null) {
// given combination already exists
throw new NonUniqueNameException(..);
// or modify the slug
$page->setSlug($page->getSlug() . '-2');
return $this->persistPage($page);
}
return $this->em->persist($page);
}
// PagesRepository::findOneBySlugs($userSlug, $pageSlug)
public function findOneBySlugs($userSlug, $pageSlug) {
$query = $this->em->createQueryBuilder('p')
->addSelect('u')
->join('p.user', 'u')
->where('p.slug = :pageSlug')
->where('u.slug = :userSlug;)
->getQuery();
$query->setParameters(combine('userSlug', 'pageSlug'));
return $query->getSingleResult();
}

Related

Symfony - How to use LIKE with COALESCE in a Doctrine request?

I have some trouble with LIKE and COALESCE in a Doctrine request (not 100% sure that the trouble is there).
I would like to search inside a database with a filter that match only if exist, and only with a part of the value (for example find 'abc' with the filter set to 'ab').
This request works fine :
public function findUsers($entreprise, $filtres)
{
$filtre_name = $filtres['name'];
return $this->createQueryBuilder('users')
->where('users.entreprise = :entreprise')
->andWhere('users.name = COALESCE(:filtre_name, users.name)')
->setParameter('entreprise', $entreprise)
->setParameter('filtre_name', $filtre_name)
->orderBy('users.name', 'ASC')
->getQuery()
->getResult();
}
It return all the users of the company "entreprise" where "filtre_name" match (if not null) with "name" in the database. (If "filtre_name" is null, then the where match for all the database thanks to COALESCE).
I would like now to do the same thing but with "LIKE" instead of "=" because for now the name has to match perfectly and I would like a match for "abc" with only "ab" inside the filter for example.
public function findUsers($entreprise, $filtres)
{
$filtre_name = $filtres['name'];
return $this->createQueryBuilder('users')
->where('users.entreprise = :entreprise')
->andWhere('users.name LIKE COALESCE(:filtre_name, users.name)')
->setParameter('entreprise', $entreprise)
->setParameter('filtre_name', '%'.$filtre_name.'%')
->orderBy('users.name', 'ASC')
->getQuery()
->getResult();
}
The result is an error : "Warning: Undefined property: Doctrine\ORM\Query\AST\CoalesceExpression::$type".
I have find a solution. I build my queryBuilder with parameter only if they are not Null. So I remove COALESCE and now I can use LIKE.
public function findUsers($entreprise, $filtres)
{
$filtre_nom = $filtres['nom'];
$filtre_gestionnaire = $filtres['gestionnaire'];
$qb = $this ->createQueryBuilder('users');
$qb ->where('users.entreprise = :entreprise')
->setParameter('entreprise', $entreprise);
if ($filtre_nom != Null) {
$qb ->andWhere('users.nom LIKE :filtre_nom')
->setParameter('filtre_nom', '%'.$filtre_nom.'%');
}
if ($filtre_gestionnaire != Null) {
$qb ->andWhere('users.gestionnaire LIKE :filtre_gestionnaire')
->setParameter('filtre_gestionnaire', '%'.$filtre_gestionnaire.'%');
}
$qb->addorderBy('users.nom', 'ASC');
return $qb ->getQuery()
->getResult();
}

ParamConverter: inject curent user in Repository

In a view, I want to display linked values, but all of the linked values can't be displayed because they depends to the user access.
To do that I just need to do a leftJoin with a ->where('user', $user). The question is... how can I inject the current user in the Repository from the ParamConverter ?
Assuming you are using Doctrine, this should work;
In your controller;
$objRepo = $this->getDoctrine()->getManager()->getRepository('AppBundle:Objects');
$files = $this->objRepo->getAllForUserId($this->getUser()->getId());
And in your repo file;
public function getAllForUserId($user_id, $limit=100)
{
if (null === $user_id) {
throw new ORMInvalidArgumentException('User id not set');
}
$queryBuilder = $this->createQueryBuilder('obj');
$queryBuilder->select(array('obj', 'usr'))
->innerJoin('obj.users', 'usr')
->where('usr.id = :user_id')
->setParameter(':user_id', $user_id)
->orderBy('file.created', Criteria::DESC);
$query = $queryBuilder->getQuery();
return $query->getResult();
}

Get custom getter property into QueryBuilder object?

I have the following QueryBuilder snippet working as expected:
$restaurants = $qb->select('r.id, r.name, r.addressGpsLat, r.addressGpsLng, r.addressZip, r.addressCity')
->from('BackendBundle:Restaurant', 'r')
->orderBy('r.name', 'ASC');
In our Restaurant entity we calculate ratings based on a relation, which looks like this:
public function getRating()
{
$rating = 0;
$votes = count($this->votes);
foreach ($this->votes as $vote) {
$rating += $vote->getRating();
}
if ($votes == 0) {
return null;
}
return number_format(($rating / $votes), 2, ',', '.');
}
So my question: is there a way I get the result of the getRating() property into my QueryBuilder object? We need it to be a seperate attribute in our JSON response. E.g. { …, "rating":2.2, …}
Any advice on this? Thanks!

Get related record one to many relationships Symfony2

I want to check if the current user has already a record for the current date.The User entity has many Timerecord and Timerecord has one User. So far,
This two entities resides in two different bundles
Project
UserBundle
TimerecordBundle
controller
$user = $this->container->get('security.context')->getToken()->getUser();
$userr = $user->getUsername();
$alreadyLoggedIn = $em->getRepository('EmployeeBundle:Timerecord')->findAlreadyTimedInToday($userr);
var_dump($alreadyLoggedIn);
die();
Respository
public function findAlreadyTimedInToday($userr)
{
return $this
->createQueryBuilder('t')
->select('u.username')
->from('User u')
->join('u.Timerecord t')
->where('u.username LIKE :currentuser')
->setParameter('currentuser',$userr)
->getQuery()
->getSingleResult()
;
}
I got this exception
Warning: Missing argument 2 for Doctrine\ORM\QueryBuilder::from()
How do you fetch the related user in this case?
The alias for the from part should be given to the second argument.
Change your method to this:
public function findAlreadyTimedInToday($userr)
{
return $this
->createQueryBuilder('t')
->select('u.username')
->from('User', 'u')
->join('u.Timerecord', 't')
->where('u.username LIKE :currentuser')
->setParameter('currentuser', $userr)
->getQuery()
->getSingleResult()
;
}

Return entity record value based on custom value in Symfony2

I'm making an API and I need to display data from entity based on action type. For example, I have User and his visibility preferences (to hide/show his name for other people). Doing this like that:
<?php
// entity
public function getSurname()
{
$visibility = $this->getVisibility();
if($visibility['name'] == 0)
return $this->surname;
return '';
}
is ok, but if User is logged in, I want to show him his name, for example, in edit account.
The best way I think is to edit record when I get it from database, but how to this on doctrine object?
<?php
//controller
$user = $this->getDoctrine()->getRepository('AcmeDemoBundle:User')->findOneById($id);
$user = $this->getVisibility();
if($user != $this->getUser() && $visibility['name'] == 0)
$user->setSurname(''); //but this save this to DB, not to "view"
UPDATE
Unfortunately (or I'm doing something wrong) my problem can't be solved by Snake answer, beause when I do this code:
<?php
$user = $this->getDoctrine()->getRepository('AcmeDemoBundle')-findOneById($id);
return array(
self::USER => $user
);
In my API response, entity modifications don't work, because I think this is getting record directly from DB? And I need return whole object like in code above.
UPDATE2
I found workaround for this
<?php
// entity
/**
* #ORM\PostLoad
*/
public function postLoad() {
$this->surname = $this->getSurname();
}
and then I can just return full $user object
If you want to show the surname depends of visibility, you can add the Symfony\Component\Security\Core\User\EquatableInterface and edit your function:
// entity
public function getSurname(Acme\DemoBundle\User $user = null)
{
// Nothing to compare or is the owner
if( !is_null( $user ) && $this->isEqualTo($user) ){
return $this->surname;
}
// else...
$visibility = $this->getVisibility();
if($visibility['name'] == 0)
return $this->surname;
return '';
}
After in your controller you only must get the surname:
//controller
$user = $this->getDoctrine()->getRepository('AcmeDemoBundle:User')->findOneById($id);
// If the user is the owner, show the surname, otherwise it shows the surname depends of visibility
$surname = $user->getSurname( $this->getUser() );
Also, you can execute the logic in the controller (check if is the same user and get the visibility...).
I suggest you read about ACL too.

Resources