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