Doctrine2 update many-to-many relations - orm

I hava relations Many-to-Many with Product entity and Feature entity
Product entity:
/**
* #ORM\ManyToMany(targetEntity="Feature")
* #ORM\JoinTable(name="Product_Feature",
* joinColumns={#ORM\JoinColumn(name="Product_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="Feature_id", referencedColumnName="id")}
* )
*/
private $features;
Feature entity:
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="features")
* #ORM\OrderBy({"position" = "ASC"})
*/
private $products;
ProductRepository.php:
public function updateFeatures($id, $featuresIds)
{
return $this->getEntityManager()->createQueryBuilder()
->update('TestCatalogBundle:Product', 'p')
->set('p.features', ':features')
->where('p.id = :id')
->setParameter('features', $featuresIds)
->setParameter('id', $id)
->getQuery()
->getResult();
}
But when I call updateFeatures I get error:
features = :features': Error: Invalid PathExpression.
StateFieldPathExpression or SingleValuedAssociationField expected
How can I update Product_Feature table? Also I can't delete all features from Product_Feature by product's id.
I changed my controller in next way:
$em = $this->getDoctrine()->getEntityManager();
$features = $em->getRepository('TestCatalogBundle:Feature')->findBy(array('id' => $featureIds));
$product = $em->getRepository('TestCatalogBundle:Product')->find($id);
$product->getFeatures()->clear();
foreach ($features as $feature) {
$product->addFeature($feature);
}
$em->persist($product);
$em->flush();
But if I use native sql I need 2 queries for deleting features and insert new features. But here I need 2 select queries. Maybe I made this task wrong?

You're doing it the wrong way. You should read this chapter of the documentation: Working with associations. You should add an "inversedBy" keyword in the $features field of the Product class.
When you have a bi-directional many-to-many relation, the usual way to do this is:
$product->getFeatures()->add($feature); // Or $product->setFeatures($features);
$feature->getProducts()->add($product);
$em->persist($product);
$em->persist($feature);
$em->flush();

Related

How to get following users posts in Laravel 5.8

I have two models in my Laravel 5.8 project, relationships is shown below in both model classes. How can I get every single post records related with every single user that I follow using just a single sql query ? Can I get it using Eloquent Query Builder or I need a Raw SQL Query ? Can Someone show me the SQL query to do it ?
Sorry, I didn't know what title to put in the question.
Thanks in advance !
User Class.
class User extends Authenticatable implements MustVerifyEmail{
use Notifiable, MessageAccessible, TagsCreator;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
"lastname",
"country",
"city",
"phone_number",
'e_mail',
'password',
"role_id",
"profile_picture",
"occupation",
"biography"
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token'
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = ['email_verified_at' => 'datetime'];
public function posts(){
return $this->hasMany(Post::class);
}
public function followers(){
return $this->belongsToMany(User::class, 'follower_followed', 'followed_id', 'follower_id');
}
public function following(){
return $this->belongsToMany(User::class, 'follower_followed', 'follower_id', 'followed_id');
}
}
Post Class.
class Post extends Model{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_id',
"post_permission_id",
"title",
"content",
"likes",
"dislikes"
];
public function user(){
return $this->belongsTo(User::class);
}
}
I think what you need to do is this.
Not sure but in some Laravel versions you have to use select instead of pluck here. This defo works in 5.6
$posts = Post::whereIn('user_id', User::find($user_id)->followers->pluck('id'))->get();
then you might wanna order the posts by follower
$posts = Post::whereIn('user_id', User::find($user_id)->followers->pluck('id'))->orderBy('user_id', 'ASC')->get();
Here is the Documentation for whereIn Scroll a bit down, there is no direct anchor on whereIn :-)
After finding/selecting the user, you can get the related posts with something like this:
$user = Auth::user(); // selecting the logged in user
$user->posts;
For this to work, you must have user_id column in the posts table.
And if you want to have all users and their posts, you can do this:
$usersWithPosts = User::with('posts')->get();
This will return all users (whether they have any post or not) if you just want users that essentially have at least one post do this:
$usersWithPosts = User::has('posts')->get();
$following_data=['0'=>'2','1'=>'3','2'=>'5'];
$follower_post =DB::table('channel_posts')
->join('users','channel_posts.user_id','users.id')
->join('category','channel_posts.category_id','category.id')
->join('country','channel_posts.country_id','country.country_id')
->select('channel_posts.*','users.name','category.name','country.name')
->whereIn('channel_posts.user_id',$following_data)
->inRandomOrder()
->limit(10)
->get();
// dd($follower_post);

Laravel/SQL: How to fetch data from multiple table in a single query? that too using 'where'

Working on a search functionality on Laravel App(Blog/Posts).
There are multiple types of posts (each having a separate table in the database)
Like Business posts, Social Life posts etc..
Below is the search function on SearchController
class SearchController extends Controller
{
public function search(Request $request, $query = null)
{
if($query == null)
return redirect()->route('home');
$search = Business::where([['title','like','%'.$query.'%'],['status','=',1]])
->orWhere([['description','like','%'.$query.'%'],['status','=',1]])
->paginate(10);
return view('front.search',[
'results' => $search,
'query' => $query
]);
}
}
So basically my question is how to add other types of Post's table also?
My main motive is that when someone searches for anything, the result should be fetched from all types of posts table(business, nature, life & so on..).
You have to maintain common id in both the table
NOTE: Join is the preferable method
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);
If you have a model called Book, like this:
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
Then you can retrieve all of your books with authors like this:
$books = App\Book::with(['author'])->get();
Check out Eager loading from Laravel documentation.
Just add table name before every field
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);

How to Join with Native SQL Query and doctrine

I'm developping an application with symfony 3.4. I want to execute a specific query. So i have two entities: the first is PrPurchaseRequest. the second is PrSpecificFieldValue. PrPurchaseRequest has oneToMany prSpecificFieldValues.
I want to get id of purchaseRequest and prSpecificFieldValues
i did that
$queryBuilder = $this->getEntityManager()->createQuery('select p.id as purchaseId, pr.keyField AS keyField,pr.ID AS prkeyvalueid from '.PrPurchaseRequest::class. ' p LEFT JOIN '. PrSpecificFieldValue::class .' spec ON p.id = spec.purchaseId ');
and that didn't work for me
[Syntax Error] Error: Expected end of string, got
'ON'
how can i do it
Using doctrine you need to play around your entities and their mappings with other entities in order to relate them like
use Doctrine\Common\Collections\ArrayCollection;
/** #Entity */
class PrPurchaseRequest
{
/**
*
* #OneToMany(targetEntity="PrSpecificFieldValue", mappedBy="prPurchaseRequest")
*/
private $prSpecificFieldValues;
// ...
public function __construct() {
$this->prSpecificFieldValues = new ArrayCollection();
}
}
/** #Entity */
class PrSpecificFieldValue
{
/**
*
* #ManyToOne(targetEntity="PrPurchaseRequest", inversedBy="prSpecificFieldValues")
* #JoinColumn(name="pr_purchase_request_id", referencedColumnName="id")
*/
private $prPurchaseRequest;
}
Now you have defined relationship between your entities you can join them based on their mapping (prSpecificFieldValues defined on PrPurchaseRequest class ) like
Its DQL (DQL != SQL)
SELECT p,v
FROM PrPurchaseRequest p
JOIN p.prSpecificFieldValues v
No need to specify ON clause doctrine will handle this for you.
One-To-Many, Bidirectional

Finding an entity that contain only some others entities

I have an association like this :
Chatroom >----< User
So a Chatroom can contains multiple users, and a User can belong to multiple Chatrooms.
Now I want to select all the chatrooms that contains a couple of user, and only this couple.
I tried some solutions, like this one :
public function findByUsers($firstUser, $secondUser){
$qb = $this->createQueryBuilder('c');
$qb
->select('c')
->where('c.users LIKE :firstUser')
->andwhere('c.users LIKE :secondUser')
->setParameters(array(
'firstUser' => $firstUser,
'secondUser' => $secondUser
));
return $qb->getQuery()->getResult();
}
But It doesn't work and return me that kind of error :
[Semantical Error] line 0, col 52 near 'users LIKE :firstUser': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
Some users encountering this error resolved it by adding IDENTITY before the query selector, but I don't understand how to apply it in my case.
So, did someone know how I can get all the chatrooms containing my couple of users ?
Thanks a lot !
EDIT : Adding the doctrine relation annotations
User.php
/**
*
* #ORM\ManyToMany(targetEntity="Chatroom", inversedBy="users")
* #ORM\JoinTable(name="chatrooms_users",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="chatroom_id", referencedColumnName="id")}
* )
*/
private $chatrooms;
Chatroom.php :
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="chatrooms")
*/
private $users;
My final solution was :
public function findByUsers($ids)
{
$count = count($ids);
$qb = $this->createQueryBuilder('chatroom');
$qb->select('chatroom')
->join('chatroom.users', 'u')
->addSelect('COUNT(u) AS HIDDEN ucount')
->groupBy('chatroom.id')
->having('ucount = :count')
->setParameter('count', $count);
foreach ($ids as $key => $id) {
$qb->andWhere(':id' . $key . ' MEMBER OF chatroom.users')
->setParameter('id'.$key, (int) $id);
}
return $qb->getQuery()->getOneOrNullResult();
}
Pass an array of users id (or simply users with some modifications), and the function returns the list of chatrooms that contains only these users

Doctrine Many to Many relations

i edit the thread for put more info.
I have the "User" entity and the "Rol" entity, and i am fighting to do work the collection of Roles of a User.
In the User entity i defined:
/**
* #ManyToMany(targetEntity="AppsManantiales\CommonBundle\Entity\Perfil")
* #JoinTable(name="usuarios_perfiles",
* joinColumns={#JoinColumn(name="idUsuario", referencedColumnName="idusuario")},
* inverseJoinColumns={#JoinColumn(name="idPerfil", referencedColumnName="idperfil")}
* )
*/
protected $perfiles;
And in the constructor:
public function __construct(){
$this->perfiles = new \Doctrine\Common\Collections\ArrayCollection();
$this->contacto = new \Doctrine\Common\Collections\ArrayCollection();
}
Before the class namespace put:
use AppsManantiales\CommonBundle\Entity\Perfil;
When execute:
php app/console generate:doctrine:entities CommonBundle
An error appear:
[Doctrine\Common\Annotations\AnnotationException]
[Semantical Error] The annotation "#ManyToMany" in property AppsManantiales\CommonBundle\Entity\Usuario::$perfiles was never impo
rted. Did you maybe forget to add a "use" statement for this annotation?
Any ideas ?.
First part: So in this case, u got a relation many-to-many between Role entity and User entity. First of all, check, r entities correct after generting. Here u can find examples of establishing different realtions: http://docs.doctrine-project.org/en/latest/reference/association-mapping.html && http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html (the second has more information with examples of Doctrine queries)
Second part of your question: after establishing right relations, select query of your User gonna be smth like:
$user = $em->createQueryBuilder()
->select('u, r')
->from('YourBundle:User', 'u')
->innerJoin('u.roles', 'r')
->where('u.id IN (:ids)')
->setParameter('ids', $ids)
->getQuery()
->getResult();
And as u guess, u can get roles with the help of your accessor: $user->getRoles()
p.s. yes, ofcource if all entities r correct, u can add methods manually.
EDITED
Oh sry, I forgot, u use Symfony2. So by default in your entities, u got such line:
use Doctrine\ORM\Mapping as ORM;
As u can notice, all annotations u used were with prefix #ORM\. exmpls:
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
So just add prefix #ORM and the result:
/**
* #ORM\ManyToMany(targetEntity="AppsManantiales\CommonBundle\Entity\Perfil")
* #ORM\JoinTable(name="usuarios_perfiles",
* joinColumns={#ORM\JoinColumn(name="idUsuario", referencedColumnName="idusuario")},
* inverseJoinColumns={#ORM\JoinColumn(name="idPerfil", referencedColumnName="idperfil")}
* )
*/