Getting specific value when using eloquent from Laravel - laravel

I am using Laravel 5.2 and I need to get specific values from the database with a leftjoin. The code I am using is as follow:
public function commentList(Request $request)
{
$inputs = $request->all();
$commentList = Comment::select(
'projects_comments.id as comment_id',
'u.name as user_name',
'projects_comments.comment as comment',
'projects_comments.created_at as created_at'
);
$commentList->leftjoin('users AS u', 'projects_comments.user_id', '=', 'u.id');
if (!empty($inputs['project_ids'])) {
$commentList->where(function ($query) use ($inputs) {
foreach ($inputs['project_ids'] as $i) {
$query->orWhere('projects_comments.project_id', $i);
}
});
};
$data = $commentList->get();
return $data;
}
It works fine but I would like to know if there is a better way to do this using eloquent but I can't really understand how to write this for eloquent to work. I need to get all the comments from an array of project ids.
I have the following model for Comment:
class Comment extends Model
{
protected $table = 'projects_comments';
public $timestamps = true;
protected $guarded = ['id'];
public function project()
{
return $this->belongsTo('App\Project', 'project_id');
}
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
}

I assume what you want is to get Comments (with their users) that belongs to specific Projects provided by the user as an array of IDS
Comment::whereIn('project_id', $inputs['project_ids'])->with('user')->get();
And if you only want the id and name of the user associated with the comment, pass the fields to the with function like so
Comment::whereIn('project_id', $inputs['project_ids'])
->with('user:id,name')->get();

Related

Laravel - How to use eloquent ORM to populate a foreign key column when getting all results from a table?

I have setup my model as following:
class Items extends Model {
use HasFactory;
protected $table = 'item';
protected $primaryKey = 'id';
protected $connection = 'mysql';
public $timestamps = false;
protected $fillable = ['user_id', 'title', 'desc', 'start_datetime', 'due_datetime', 'priority', 'status'];
public function getManager() {
return $this->belongsTo(User::class, 'user_id');
}
public function getAssignees() {
return $this->belongsToMany(User::class);
}
}
I am getting all items using the controller method below, what I want to do is to populate the user_id field in each of the items using getManager() method I declared in my Item model. I know how to do this when getting only one item, but how to populate every record when getting all of them?
public function getall() {
try {
$items = Item::get();
return response()->json(['items' => $items], 200);
} catch (Throwable $err) {
return response()->json($err, 400);
}
}
I have tried this but no luck:
public function getall() {
try {
$items = Item::get();
$items = array_map(function ($el) {
return $el->manager = $el->getManager()->get();
}, $items);
return response()->json(['items' => $items], 200);
} catch (Throwable $err) {
return response()->json($err, 400);
}
}
There are a few things here that I have some concerns about. Your code may work, but you are also doing more than you need to and not using Laravel how it was meant to be used.
Model Name
Your model name is Items, but it should be singular, Item. This helps Laravel automate things so you have less work to do.
https://laravel.com/docs/8.x/eloquent#eloquent-model-conventions
class Item extends Model {
Database Settings
You've set the $table, $primaryKey, and $connection attributes, but these should be automatic. You can probably remove them.
protected $table = 'items'; // assuming your model name is Item, this would automatically be 'items'
protected $primaryKey = 'id'; // default is already 'id'
protected $connection = 'mysql'; // default is your main db, probably already 'mysql', unless if you have multiple db connections
Timestamps
I'm not sure why you'd want to turn timestamps off. You definitely can but I always find it helpful to know when something was created or last updated. Since Laravel handles the timestamps for you, I'd suggest leaving it on, but it's up to you.
https://laravel.com/docs/8.x/eloquent#timestamps
public $timestamps = false;
Manager Relationship
Your manager relationship is getManager but should just be manager. It will still work, but isn't how Laravel was meant to work. I would suggest changing it to manager(), and not specifying the column name. This would make the column name automatically manager_id, so you'd have to update that. Or you can keep the column name 'user_id'.
https://laravel.com/docs/8.x/eloquent-relationships#one-to-many-inverse
public function manager() {
return $this->belongsTo(User::class);
}
Assignees Relationship
Same as with the Manager relationship, you should change getAssignees() to assignees(). I'm assuming you already have a database migration set up for your 'item_user' table that Laravel will look for. If not, check the Laravel docs on how to set it up.
https://laravel.com/docs/8.x/eloquent-relationships#many-to-many
public function assignees() {
return $this->belongsToMany(User::class);
}
Retrieving Items
Finally, with the above changes, getting all Items should be easy. To load the relationships, use the $with method. This is called Eager Loading. Check the docs for more info.
https://laravel.com/docs/8.x/eloquent-relationships#eager-loading
$items = Item::with('manager','assignees')->get();
Returning Response Codes
You were returning your responses incorrectly. You do not need to set the response code 200, as this is the default. If you are going to set it to something else, put the code in the response() method, instead of the json() method.
https://laravel.com/docs/8.x/responses
return response()->json(['items' => $items]);
return response($err,400);
Now putting it all together, your Item model should look something like this:
class Item extends Model {
use HasFactory;
protected $fillable = ['manager_id', 'title', 'desc', 'start_datetime', 'due_datetime', 'priority', 'status'];
public function manager() {
return $this->belongsTo(User::class);
}
public function assignees() {
return $this->belongsToMany(User::class);
}
}
public function getall() {
try {
$items = Item::get()
->transform(function($el){
$el->manager = $el->getManager()->get();
);
return response()->json(['items' => $items], 200);
} catch (Throwable $err) {
return response()->json($err, 400);
}
}
Try the transform method on your results and it would work.
https://laravel.com/docs/8.x/collections#method-transform
the transform function would basically just iterate over the results and do whatever it is told to like a for loop but for collections.
Also, to make your query efficient avoid the use of loading the relation in the transform function and and use with function of laravel to make it efficient

In Laravel - Add a variable to a model, without putting it in the database

I have a Team-model that has been used several places, and which returns the fields from the database in an API-endpoint.
It's currently accessed and returned like this:
$team = Team::find(1)
return $team;
I would like to add a calculated variable to that returned Collection. I imagined that I could add it to the constructor of the Model, and thereby get it with all the places where the Team-model is currently used, like this:
class Team extends Model
{
protected $table = 'teams';
protected $fillable = [
'id',
'created_at',
'updated_at',
'team_name'
];
public $number_of_players;
public function __construct( array $attributes = [] ){
$this->number_of_players = 3; //This number should be calculated
parent::__construct( $attributes );
}
}
But that doesn't work.
How do I add a variable to all the places, where the Team-model is fetched?
I also looked into API Resources. I looks like that that could be a solution, but I found it pretty verbose and a long-haired solution (plus, I couldn't get it to work either).
You can use accessor/mutator
Suppose you have a relationship
Team->Player (Team hasMany Players)
You can do like
in Team model
class Model extends Model {
public function players()
{
return $this->hasMany(Player::class, 'team_id', 'id');
}
}
now you can make it
<?php
class Model extends Model {
protected $appends = ['number_of_players'];
public function players()
{
return $this->hasMany(Player::class, 'team_id', 'id');
}
public function getNumberOfPlayersAttribute()
{
return $this->players->count();
}
}
And then access the players count of a team like App/Team::find(1)->number_of_players

Merging two queries into one using Eloquent

I have this two queries to display information in 3 tables Users, Comments, and Posts. so I made this function:
public function show($id)
{
$posts = Post::with(['comments'])->findOrFail($id);
$user = User::find($posts->user_id);
echo "<h1>".$posts->title.'</h1>';
echo "<h2>".$user->name.'</h2>';
foreach ($posts->comments as $comment) {
echo $comment->body.'<br>';
}
}
on this function I using two variable $posts and $user, can I merge this two variable using eloquests command like Post::with(['user','comments'])'? so I can use just $posts variable and use it like $posts->users->name to access user name.
i was trying using this way:
$posts = Post::with(['comments','users'])->findOrFail($id);
but when i echo the post it showing that the user was null:
{"id":1,"user_id":1,"title":"Oleh id 1","body":"ini adalah content","created_at":"2017-10-18 03:25:54","updated_at":"2017-10-18 03:25:54","comments":[{"id":1,"post_id":1,"body":"Keren banget deh artikelnya, TOP","created_at":"2017-10-18 03:43:50","updated_at":"2017-10-18 03:43:50"},{"id":2,"post_id":1,"body":"Keren banget deh artikelnya, TOP","created_at":"2017-10-18 03:43:53","updated_at":"2017-10-18 03:43:53"},{"id":3,"post_id":1,"body":"Keren banget deh artikelnya, TOP","created_at":"2017-10-18 03:43:54","updated_at":"2017-10-18 03:43:54"}],"users":null}
Here's my model if you need it. My post model:
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
public function users(){
return $this->belongsTo('App\User');
}
}
my Comment model
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
You’ll have an easier time sticking to Laravel’s conventions for naming relationships.
A Post has many Comments, which belong to a User. Given this set up:
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
You can then query a post’s comments and users like this:
$comments = $post->comments()->with('user')->get();
You could also eager-load the user relation on comments if you always wanted the user returned with a comment:
class Comment extends Model
{
protected $with = ['user'];
public function user()
{
return $this->belongsTo(User::class);
}
}
Then your query would be simplified:
$comments = $post->comments;
Hope this helps you!!
What I am guessing is you want the list of all posts with the users. If relation is defined, try with the join
$posts = DB::table('posts')
->join('users', 'users.id', '=', 'posts.user_id')
->select('posts.*', 'users.name as user_name')
->where('posts.comments', '<>', NULL)
->get();
#NOTE: If you are using soft deletes you might wanna add where('posts.deleted_at', null)
After couple of days searching finally i got the answer, i can get merge the query like this:
$posts = Post::with(['comments','users'])->findOrFail($id);
but in the Post Model the function not "Users" but "User".

Update index algolia

I'm using Laravel 5.5 with Scout. I have an index in algolia using Documents and the users associated with theses Documents
class Documents extends Model
{
use Searchable;
public function toSearchableArray()
{
$data = $this->toArray();
// formatting relationship for algolia
$data['users'] = $this->types->toArray();
$data['document_type'] = $this->typeDocuments->name;
return $data;
}
protected $fillable = array('name', 'description', 'document_type');
public function types() {
return $this->belongsToMany('App\Users', 'document_rights', 'user_id', 'id');
}
public function typeDocuments() {
return $this->belongsTo('App\Document_type', 'document_type');
}
}
In my case, I will update one day the name of the Users, like this:
public function update(Request $request)
{
$user = Users::find(5);
$user->name = 'jean floriot';
$user->update();
}
But it never changes the User in the index of Algolia. Any ideas how to proceed ?
I believe you can use the searchable() method for that. After $this->update() you will get something like:
App\Documents::where('user_id', '=', $this->id)->searchable();
Please let me know if that worked or if I missed something.

Need to take a sample of all positions where user_id is not equal to ours

Have 3 tables
users
posts
post_user (id, post_id, user_id)
class Post extends Eloquent {
protected $table = 'posts';
public $timestamps = true;
public function users()
{
return $this->belongsToMany('User');
}
}
class Users extends Eloquent {
protected $table = 'users';
protected $hidden = array('password', 'remember_token');
public function posts()
{
return $this->belongsToMany('Post');
}
}
Controller
public function application()
{
$posts = Post::find(1);
$user = DB::table('post_user')->where('user_id', '=', $this->id)->lists('user_id');
$posts = Post::whereNotIn('id', $user)->get();
return View::make('applications')->with(array('posts' => $posts));
}
What am I doing wrong? If possible with an explanation
You probably want to do lists('post_id').
However there is a much nicer way with whereDoesntHave:
$userId = $this->id;
$posts = Post::whereDoesntHave('users', function($q) use ($userId){
$q->where('user_id', $userId);
})->get();
Assuming that $this->id contains your user id, try this:
$posts = Post::whereHas('users', function($q) {
$q->whereNotIn( 'id', [$this->id])
})->get();
Method whereHas() selects posts belonging users, which have met a condition within the Closure. And this condition - method whereNotIn() - check if user id is different form $this->id.

Resources