I am fairly new to laravel and eloquent.
I have 2 tables threads and messages you can see the structure in the links below:
threads
messages
Now my goals is to query only the threads from table threads that have the same user_id in the messages table as the logged in user .
Is there a way to do this via eloquent or do I have to write a query for this?
I currently get all the threads like this:
$thread = Thread::findOrFail($id);
But this gives security issues since you can go to any thread if you change the id in my route.
EDIT
My current show function:
public function show($id)
{
$currentUserId = Auth::user()->id;
$threads = Thread::forUser($currentUserId)->latest('updated_at')->get();
try {
$thread = Thread::findOrFail($id);
} catch (ModelNotFoundException $e) {
Session::flash('error_message', 'Oops, not found.');
return redirect('messages');
}
if(array_has($threads, $thread)){
$users = User::whereNotIn('id', $thread->participantsUserIds($currentUserId))->get();
$thread->markAsRead($currentUserId);
return view('messenger.show', compact('thread', 'users'));
}else{
Session::flash('error_message', 'Oops, not found.');
return redirect('messages');
}
}
I need a way to check if $thread is inside $threads.
If you want to use eloquent you must first define a relationship.
One message belongs to a thread and a user. Here is how to define the relationships:
Inside the Message model:
public function user()
{
return $this->belongsTo('App/User'); //User model
}
public function thread()
{
return $this->belongsTo('App/Thread'); //Thread model
}
To define the inverse you do the following:
Inside User model:
public function threads()
{
return $this->hasMany('App/Thread');
}
Inside the Thread model:
public function messages()
{
return $this->hasMany('App/Message');
}
Now you can do the following in your controller:
$threads = Auth::user()->threads;
Now you have all threads by the currently logged in user.
I am not sure if I got the question right so ask away.
Edit:
You could check like so:
$thread = Thread::find($id);
$isCurrentUserThread = false;
foreach(Auth::user()->threads as $currentUserThread) {
if($currentUserThread->id == $thread->id) {
$isCurrentUserThread = true;
//$thread belongs to the current user
}
}
if($isCurrentUserThread) {
//the thread belongs to the current user
} else {
//it doesn't belong to the current user
}
There is a way to get the current user's id by calling:
$logged_in_user = Auth::user()->id
Just make sure to include this portion somewhere on top:
use Illuminate\Support\Facades\Auth;
Then, you could just get all Messages where the user_id equals to the logged in user...
$messages = App\Message::where('user_id', '=', $logged_in_user)->get();
// the get() method will get all messages, not just one
From there, you could extract the $messages variable and grab all the thread_ids, which then, you could use the find method on the Thread model, like the following:
$threads = App\Thread::find([1, 2, 3, 4, ...]);
Related
For example:
when a user logged or registered in for the first time and didnt make a profile yet is there anyway u can check where ever he made a profile so u can send him an alert to make one incase he didnt make a profile yet.
Does laravel have Helper functions for that or u need to do someting like this?
if(\Auth::check())
{
$user_id = \Auth::user()->id;
$profile = Profile::where('id', $profile->id)->where('user_id', $user_id)->first();
if($profile->count() > 0 ) {
return false;
} else {
return true;
}
}
Assuming you want a Profiletable to hold information from the User without merging them together, you will want to:
Run on the project root folder:
php artisan make:migration:schema create_profiles_table --schema="user_id:integer:foreign, title:string"
Create the relationship:
User Model
public function profiles()
{
return $this->hasOne('App\Profile', 'id', 'user_id');
}
Profile Model
public function users()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
Check if a User have a profile:
In your controller
if (is_null(Auth::user()->profiles)) {
//your logic here
}
I have two models and two tables. First model name Inbox with database structure:
And second model name is StudentData with database structure:
And I will return with route('/sended') all mesages using my method for get needed messages:
public function getMessages($message_type = "new")
{
$user_id = Auth::user()->id;
$inbox = new Inbox();
$paginate = 3;
switch ($message_type) {
case 'sended':
$messages = $inbox->where('sender', $user_id)
->where('trashed_in_sender', 0)
->where('show_in_sender', 0)
->orderBy('created_at', 'desc')
->paginate($paginate);
break;
default:
return abort(404);
break;
}
return $messages;
}
And I have methods in my model Inbox:
public function messageSender()
{
return $this->belongsTo("App\StudentData", 'sender');
}
public function messageRecipient()
{
return $this->belongsTo("App\StudentData", 'recipient');
}
When I call in view $message->messageSender in result given NULL. Why I can't get data using sender id from inboxes table in student_datas table
So, I have a few questions....
1) How exactly does your User and StudentData models interact? It kinda seems strange to have 2 models with what seems to be a 1:1 relationship?
Why not just use a User model?
(Do you even have a User model or am I misinterpreting things?)
2) I think your direction is wrong... if you already have a User model, try to get the sent messages from there. I will give you an example.
Let's say you have a User model and a Inbox model, where you have a "sender" and "recipient", which both have an id of the User model.
So in the Inbox model we have:
public function messageSender()
{
return $this->belongsTo("App\User", 'sender');
}
public function messageRecipient()
{
return $this->belongsTo("App\User", 'recipient');
}
But why not go from the other direction? We can write the relationships in the User model like
public function sentMessages()
{
return $this->hasMany("App\Inbox", 'sender');
}
public function receivedMessages()
{
return $this->hasMany("App\Inbox", 'recipient');
}
Now you can get all sent messages (i.e. all messages where the user is the sender) just by using
$user->sentMessages
and operate on that. Or you could even set up a special helper relation (my name sucks, find a better one... just for example)
public function unreadSentMessages()
{
return $this->hasMany("App\Inbox", 'sender')
->where('trashed_in_sender', 0)
->where('show_in_sender', 0)
->orderBy('created_at', 'desc');
}
and can either use $user->sentMessages for all his messages or $user->unreadSentMessages for only the ones you need in your case.
I am trying to grasp the concept of Eloquent ORM by creating a ticketing system at the moment. What I am trying to achieve is:
The tickets with the user who posted the ticket
The feedback belonging to the ticket and the user who entered the
feedback
This is what I have right now:
// TicketController.php
public function index()
{
$tickets = Ticket::with('feedback')->with('user')->orderBy("created_at", "desc")->get();
//dd($tickets);
return View::make('modules.helpdesk.index')->withTickets($tickets);
}
And the following models
// Ticket.php
class Ticket extends Eloquent {
protected $table = 'helpdesk_tickets';
public function feedback()
{
return $this->hasMany('Feedback');
}
public function user()
{
return $this->belongsTo('User');
}
}
// Feedback.php
class Feedback extends Eloquent {
protected $table = 'helpdesk_tickets_feedback';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}
// User.php
class User extends Eloquent {
protected $table = 'users';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}
What I have now is the tickets, their related feedback and user who created the ticket. What I am trying to achieve now is to also get the user who created the feedback.
You need to fix the relation:
// User model
public function tickets()
{
return $this->hasMany('Ticket'); // adjust namespace if needed
}
Next add the relation:
// Feedback model
public function user()
{
return $this->belongsTo('User'); // namespace like above
}
then use eager loading:
// it will execute 4 queries:
// 1st for tickets
// 2nd for feedback
// 3rd for feedbacks' user
// 4th for tickets' user
$tickets = Ticket::with('feedback.user', 'user')->latest()->get();
you can then access the relations in a loop, like below:
#foreach ($tickets as $ticket)
{{ $ticket->title }} by {{ $ticket->user->name }}
#foreach ($ticket->feedback as $feedback)
{{ $feedback->content }}
#endforeach
#endforeach
What you want to do is create nested relations, just like Ticket add a belgonsTo relation on feeback
When you want to use it you can chain relations using the dot notation feedback.user
The code
// Feedback.php
class Feedback extends Eloquent {
protected $table = 'helpdesk_tickets_feedback';
public function ticket()
{
return $this->belongsTo('Ticket');
}
public function user()
{
return $this->belgonsTo('User')
}
}
// TicketController.php
public function index()
{
$tickets = Ticket::with('feedback')->with('user')->with('feedback.user')->orderBy("created_at", "desc")->get();
//dd($tickets);
return View::make('modules.helpdesk.index')->withTickets($tickets);
}
EDIT:
Even though this would work, it will execute more queries than needed. See Jareks answer.
Original Answer:
First of all you need to get your relationships straightened, in User.php you should call the user relationship with HasMany.
public function ticket() {
return $this->hasMany('Ticket');
}
In modules.helpdesk.index you should now have a Ticket Collection since your attaching the $ticket variable to the view.
If you loop through this collection with a foreach loop then what you should get is a model each loop:
foreach($tickets as $ticket) {
// Prints the name property of the Ticket model
print $ticket->name;
// Since a ticket only belongs to ONE user then that means that you are trying to fetch a model
// What we're doing here is getting the User model via the relationship you made in the model Ticket.php and then getting the name.
print $ticket->user()->first()->username;
// Since a ticket can have MANY feedbacks that means were fetching a collection
// which needs to be broken down to models so we do that looping the collection.
// Here we are doing the same thing as with the User model except with a collection.
foreach($ticket->feedback()->get() as $feedback) {
$feedback->text;
}
}
You should definitely check out the Laravel API and see Collection and Model there. http://laravel.com/api/ You get alot of help from there when you get stuck, trust me :)
I hope this answered your question.
I have a module of friendship request in my project. Below 3 tables are being used in it:-
Users
User_profile
Friendship
Users :- Id,slug,Name,Email, Password
UserProfile :- Id, user_slug, Profile_pic, DOB..etc.
Friendship :- Id, User_slug, Friend_slug, Status
Relationships:-
User Model:-
public function Profile(){
return $this->hasOne('UserProfile','user_slug','slug')->first();
}
public function sentFriendshipRequests(){
return $this->hasMany('Friendship','user_slug','slug');
}
public function receivedFriendshipRequests(){
return $this->hasMany('Friendship','friend_slug','slug');
}
UserProfile Model:-
public function User(){
return $this->belongsTo('User','user_slug','slug');
}
Friendship Model:-
public function receiver(){
return $this->belongsTo('User','friend_slug','slug');
}
public function sender(){
return $this->belongsTo('User','user_slug','slug');
}
Goal:- I want to display list of pending friendship request received by an user.
Data Required:-
All friendship request with pending status for current logged user & Name,Slug,Profile_pic of friendship request sender.
My Approach:-
$friendship_requests= Auth::user()->receivedFriendshipRequests();
foreach($friendship_requests as $frnd_req)
{
$sender_user=User::where('slug',$frnd_req->user_slug());
}
Is there any other proper way to get this data by using Eloquent Relationship approach,without using join. I means how to get data using HasOne and HasMany relationship in one single query.
Any help or advice is greatly appreciated.
Thanks
This is a self referencing many-to-many relationship, so you don't need those hasMany/belongsTo relations at all.
You can simply use one belongsToMany for own requests and another one for received requests.
Read this first: https://stackoverflow.com/a/25057320/784588
Then add these relationships:
// pending requests of mine
function pendingFriendsOfMine()
{
return $this->belongsToMany('User', 'friendship', 'user_slug', 'friend_slug')
->wherePivot('accepted', '=', 0)
->withPivot('accepted');
}
// pending received requests
function pendingFriendOf()
{
return $this->belongsToMany('User', 'friendship', 'friend_slug', 'user_slug')
->wherePivot('accepted', '=', 0)
->withPivot('accepted');
}
// accessor allowing you call $user->friends
public function getPendingFriendsAttribute()
{
if ( ! array_key_exists('pendingFriends', $this->relations)) $this->loadPendingFriends();
return $this->getRelation('pendingFriends');
}
protected function loadPendingFriends()
{
if ( ! array_key_exists('pendingFriends', $this->relations))
{
$pending = $this->mergePendingFriends();
$this->setRelation('pendingFriends', $pending);
}
}
protected function mergePendingFriends()
{
return $this->pendingFriendsOfMine->merge($this->pendingFriendOf);
}
then yuou simply load it using nested relations:
$user = Auth::user();
$user->load('pendingFriendsOfMine.profile', 'pendingFriendOf.profile');
// the above will execute 4 queries - 2 for requests, 2 for related profiles
$pendingFriends = $user->pendingFriends; // for all pending requests
// or
// $user->load('pendingFriendOf.profile'); // 2 queries in this case
// $pendingRequests = $user()->pendingFriendOf; // for received requests only
foreach ($pendingFriends as $user) {
$user->profile; // eager loaded profie model
}
Also, here a few errors you have in your code:
// there can't be first() in the relation definition
// and it is not needed anyway
public function Profile(){
return $this->hasOne('UserProfile','user_slug','slug')->first();
}
// You never want to run this User::where() ...
// in a foreach loop, for it will result in n+1 queries issue
// You need eager loading instead.
foreach($friendship_requests as $frnd_req)
{
$sender_user=User::where('slug',$frnd_req->user_slug());
}
I have three tables - Campaigns, Actions and Activists. A Campaign has many Actions, and an Action belongs to both an Activist and a Campaign.
Each action has a client_id (from the client_id of the campaign it belongs to), so when a client views a list of activists, they should only see those who have taken an action on one of their campaigns.
Likewise, when viewing an individual activist, they should only see those actions related to their campaigns.
Models
Campaign.php
public function actions()
{
return $this->hasMany('Action');
}
Action.php
public function campaign()
{
return $this->belongsTo('Campaign', 'campaign_id');
}
public function activist()
{
return $this->belongsTo('Activist', 'activist_id');
}
Activists.php
public function actions()
{
return $this->hasMany('Action');
}
Controllers
ActivistsController.php
public function index()
{
$activists = Activist::with('actions')->whereHas('actions', function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
}))->get();
foreach ($activists as $activist)
{
$activist->total = $activist->actions()->count();
}
}
public function getActivist($id)
{
$activist = Activist::with('actions')->whereHas('actions', function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
})->find($id);
$activist->total = $activist->actions()->count();
}
I'm seeing the following:
On the /activists page, I'm correctly seeing only those activists who have taken an action related to my client_id, but also every action they've taken. Likewise, count() returns a full count of all the activists' actions.
On the /activists/{id} page, it correctly returns null if the activist hasn't taken any actions related to my client_id, but where they have, I again see all of their actions and a full count.
AFL. There's something blinding obvious I'm missing, right?
Thanks.
[edit] Updated to add:
Using the client_id filter on both with and whereHas rectifies the 'all actions appearing regardless' issue, but the count issue remains (and I'm not sure this is remotely the right way to improve this):
ActivistController.php
public function index()
{
$filter = function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
};
$activists = Activist::with(array('actions' => $filter))
->whereHas('actions', $filter)
->get();
}
public function getActivist($id)
{
$filter = function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
};
$activist = Activist::with(array('actions' => $filter))
->whereHas('actions', $filter)
->find($id);
}
I've solved this now, but for reference:
$activist->actions()->count()
This, obviously in hindsight, ignores any of the prior queries and simply counts data returned from the actions() method as defined in the activist model.
I should have provided an alternate method in the model that includes the appropriate where function, like so:
public function actionsClient($id)
{
return $this->hasMany('Action')->where('client_id', $id);
}
Meaning the count could then be invoked with:
$activist->total = $activist->actionsClient($id)->count();
for a single campaign and
foreach ($activists as $activist)
{
$activist->total = $activist->actionsClient($activist->id)->count();
}
on the index. I'd previously tried this, but as described here - How to access model hasMany Relation with where condition? - relations must be described in camelCase (actions_client > actionsClient).
In my usage this worked for me:
$clients = Profile::select('id', 'name')->orderBy('initial')->whereHas('type', function ($query) {
$query->where('slug', 'client');
})->office()->pluck('name', 'id');
You already have the instance of $activist eager loading their actions, meaning you already know the actions of the activist beacause they are already in the instance, so why call actions() again instead of just doing this:
$activist->actions->count()