Finding an entity that contain only some others entities - sql

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

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);

Moodle get SQL data but don't get all

I'm working on a plugin for showing all users completed courses.
But I only get 10 records, when I place the SQL inside my database I get 40+. I think there is a limit or it does only return 1 course from every user.
Any tips?
externallib.php file:
/**
* External Web Service Template
*
* #package specialist_in_websites
* #copyright 2011 Moodle Pty Ltd (http://moodle.com)
* #license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->libdir . "/externallib.php");
class specialist_in_websites_users_courses_external extends external_api
{
/**
* Returns description of method parameters
* #return external_function_parameters
*/
public static function hello_world_parameters()
{
return new external_function_parameters(
array()
);
}
/**
* Returns welcome message
* #return string welcome message
*/
public static function hello_world()
{
global $DB;
$sql = 'SELECT u.id as user_id, c.id as course_id, DATE_FORMAT(FROM_UNIXTIME(p.timecompleted),"%Y-%m-%d") AS completed_time
FROM mdl_course_completions AS p
JOIN mdl_course AS c ON p.course = c.id
JOIN mdl_user AS u ON p.userid = u.id
WHERE c.enablecompletion = 1 ORDER BY u.id';
$datas = $DB->get_records_sql($sql);
return (array)$datas;
}
/**
* Returns description of method result value
* #return external_description
*/
public static function hello_world_returns()
{
return new external_multiple_structure(
new external_single_structure(
array(
'user_id' => new external_value(PARAM_INT, 'user id'),
'course_id' => new external_value(PARAM_INT, 'course id'),
'completed_time' => new external_value(PARAM_TEXT, 'Time of Completed'),
)
)
);
}
}
and response code:
string(510) "[{"user_id":2,"course_id":12,"completed_time":null},{"user_id":3,"course_id":10,"completed_time":null},{"user_id":4,"course_id":9,"completed_time":null},{"user_id":5,"course_id":41,"completed_time":null},{"user_id":6,"course_id":14,"completed_time":null},{"user_id":7,"course_id":10,"completed_time":null},{"user_id":8,"course_id":9,"completed_time":null},{"user_id":9,"course_id":9,"completed_time":null},{"user_id":10,"course_id":10,"completed_time":null},{"user_id":11,"course_id":10,"completed_time":null}]"
As stated in the docs the $DB->get_records_*() functions get a hashed array of records, indexed by the first field returned.
So, if you return more than one record with the same user_id field, then they will overwrite the previous record in the array.
Make sure you turn on debugging when developing for Moodle and you will see warning messages about this.
Solutions - either find a different field to be the first field returned (e.g. course_completions.id) or use $DB->get_recordset_sql() and then loop through the results.
Also, do not use the 'mdl_' prefix in your code, as that will break on any site with a different prefix, or if you try to run any behat or unit tests on your site. As stated in the docs, write:
FROM {course_completions} p
JOIN {course} c ON p.course = c.id
etc.
(and don't use AS with tables, as that's not compatible with all DB types)

Codeigniter populate nested associations

this is the results i need from the database
{comment: 'hello there', user: [{name: 'sahan', id: 1}], id: 2}
function used to get comments
public function get_comments(){
$query = $this->db->get('comments');
return $query->result_array();
}
I have a comments table and a users table, When a user comments on something the
comment is saved as follows
comment > Comment text
user: userid
So when the data is shown I need codeigniter to populate the user field with the user data found from the users table
Does anyone know how to do this ?
I used this functionality in SailsJS but dont know how to do it here in CodeIG
Sails.js populate nested associations
Codeigniter 's active record is not as advanced as SailJS active record, but you can achieve what you are asking for with two queries within the same method.
I'm assuming the comments table has a foreign key to the users table through a user_id field.
public function get_comments() {
/* Get all the comments */
$query = $this->db->get('comments');
$comments = $query->result_array();
/* Loop through each comment, pulling the associated user from the db */
foreach $comments as &$comment {
$query = $this->db->get_where('users', array('id' => $comment['user_id']));
$user = $query->result_array();
/* Put the user's data in a key called 'user' within the comment array */
$comment['user'] = $user;
/* Remove the unnecessary user_id and $user variable */
unset($comment['user_id']);
unset($user);
}
return $comments;
}

symfony2 - Doctrine - How to do a multiple select with a count and group by

In Symfony2 and Doctrine I would like to execute a query that returns a count and a group by.
Here's what I've tried. This is the SQL I want to run:
SELECT `terrain_id` , COUNT( * )
FROM `Partie`
WHERE 1 =1
GROUP BY `terrain_id`
With my entity:
class Partie
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Gp\UserBundle\Entity\User",
inversedBy="parties", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Gp\JeuxBundle\Entity\Terrain")
*/
private $terrain;
This is my PartieRepository
public function getTest(\Gp\UserBundle\Entity\User $user){
return $this->createQueryBuilder('p')
->select('count(p), p.terrain')
->where('p.user = :user')
->setParameter('user', $user)
->groupBy('r.terrain')
->getQuery()
->getResult();
}
This is the error I get:
[Semantical Error] line 0, col 19 near 'terrain FROM': Error:
Invalid PathExpression. Must be a StateFieldPathExpression.
You'll probably want to go with a Native Query
$sql = "SELECT terrain_id as terrain,
count(*) AS count "
."FROM Partie "
."GROUP BY terrain_id;";
$rsm = new ResultSetMapping;
$rsm->addScalarResult('terrain', 'terrain');
$rsm->addScalarResult('count', 'count');
$query = $this->_em->createNativeQuery($sql, $rsm);
return $query->getResult();
Just add in any having / where clauses as needed.
The following is my result:
Array
(
[0] => Array
(
[terrain] =>
[count] => 7
)
[1] => Array
(
[terrain] => 1
[count] => 5
)
[2] => Array
(
[terrain] => 2
[count] => 1
)
)
The lack of terrain in the first array is due to null terrain_id.
EDIT
OP has unexpected results, so here are some troubleshooting steps:
1) Try a var_dump($query->getSQL()); right before the return statement, and run the SQL directly against your DB. If this produces incorrect results, examine the query and alter the $sql as appropriate.
2) If #1 produces correct results, try a var_dump($query->getResult()); right before the return statement. If this produces correct results, something is going on deeper in your code. It's time to look at why terrain is being filtered. It may be as simple as removing or changing the alias in SQL and addScalarResult.
3) Try an even simpler function:
$sql = "SELECT distinct(terrain_id) FROM Partie;";
$rsm = new ResultSetMapping;
$rsm->addScalarResult('terrain_id', 'terrain_id');
$query = $this->_em->createNativeQuery($sql, $rsm);
var_dump($query->getSQL());
var_dump($query->getResult());
return $query->getResult();
This error occurs on this line : select('count(p), p.terrain') where you are trying to use p alias that doesn't exist anymore. The select method override the default alias of the createQueryBuilder(). To avoid this, use addSelect instead or specify clearly the from method.
Try this :
public function getTest(\Gp\UserBundle\Entity\User $user){
return $this->createQueryBuilder('p')
->addSelect('count(p), p.terrain')
->where('p.user = :user')
->setParameter('user', $user)
->groupBy('r.terrain')
->getQuery()
->getResult();
}
or this :
public function getTest(\Gp\UserBundle\Entity\User $user){
return $this->createQueryBuilder('p')
->select('count(p), p.terrain')
->from('YourBundle:YourEntity', 'p')
->where('p.user = :user')
->setParameter('user', $user)
->groupBy('r.terrain')
->getQuery()
->getResult();
}
How to execute a raw Query with count and group by:
Hook onto your manager and make a new connection:
$manager = $this->getDoctrine()->getManager();
$conn = $manager->getConnection();
Create your query and fetchAll:
$result= $conn->query(
'select count(p.id) foobar, p.myid from foobartable p group by p.myid'
)->fetchAll();
Get the data out of result like this:
$this->appendStringToFile("first row foobar is: " . $result[0]['foobar']);

Doctrine2 update many-to-many relations

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();