Accessing a hasMany relation function via Collection - php

I am trying to get all of the users notifications, and depending on if the user is a buyer or seller (can be both). I have made two functions in my notifications table to filter each other out.
My goal is to ultimately run:
$notifications = Auth::user()->notifications()->getBuyerNotifications();
or
$notifications = Auth::user()->notifications()->getSellerNotifications();
I am running into an issue: Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany
User Model:
public function notifications() {
return $this->hasMany('App\Notification', 'user_id', 'id');
}
Notifications Model:
public function user() {
return $this->belongsTo('App\User', 'id', 'user_id');
}
public static function getBuyerNotifications() {
return self::whereNotNull('buyer_id')
->whereNull('deleted_at')
->get();
}
public static function getSellerNotifications() {
return $this->whereNotNull('seller_id')
->whereNull('deleted_at')
->get();
}
The command I want to run to get all of the users notifications if they're a buyer: $notifications = Auth::user()->notifications()->getBuyerNotifications();

Firstly, you don't need to use whereNull('deleted_at'), you can import the softDeletes Trait in your model:
use Illuminate\Database\Eloquent\SoftDeletes;
...
class Notification extends Model {
use SoftDeletes;
...
}
Laravel will automatically use whereNull('deleted_at') on Eloquent-Builder.
Secondly, you cannot use static method on Illuminate\Database\Eloquent\Relations\HasMany.
Use scope method instead:
public function scopeBuyerNotifications($query) {
return $query->whereNotNull('buyer_id');
}
public function scopeSellerNotifications($query) {
return $query->whereNotNull('seller_id');
}
So you can find the notification like this:
$notifications = Auth::user()->notifications()->sellerNotifications()->get();
$notifications = Auth::user()->notifications()->buyerNotifications()->get();

Auth::user() uses session data.
Try this:
optional(User::find(Auth::id())->notifications)->getBuyerNotifications;
or
$userId = 1; // Example id you can just pass the user Id.
User::find($userId)->notifications->getBuyerNotifications;

You can add two other methods in user model as follows
public function getBuyerNotifications() {
return $this->hasMany('App\Notification', 'buyer_id', 'id');
}
public function getSellerNotifications() {
return $this->hasMany('App\Notification', 'seller_id', 'id');
}
And you can call it directly from the user instance
$user->getBuyerNotifications();
$user->getSellerNotifications();

Related

Cannot access Intermediate table in HasManyThrough relationship

My DB schema looks like this.
Now, in artisan tinker mode, When I try to query Details table from user Model, it shows me the records of the details table but I cannot access the the Cases Model for some reason, it always returns NULL in tinker.
This is my User Model
public function details()
{
return $this->hasManyThrough('App\Models\Detail', 'App\Models\Cases', 'user_id', 'case_id', 'id', 'id');
}
What am I doing wrong?
If for convenience you want to access Details directly from the User model then you can define relations as - (may seem like a little duplication but worth if it results in ease)
class User extends Model
{
public function cases()
{
return $this->hasMany(Case::class);
}
public function details()
{
return $this->hasManyThrough(Detail::class, Case::class);
}
}
class Case extends Model
{
public function details()
{
return $this->hasMany(Detail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class Detail extends Model
{
public function case()
{
return $this->belongsTo(Case::class);
}
}
Now both cases and details can be directly accessed via User record
$user->cases;
$user->details;
The idea of hasManyThrough is to skip the intermediate table. If you need to look at the cases and the details maybe you should define other relations for it.
// User model
public function cases()
{
return $this->hasMany(Cases::class, 'user_id');
}
// Cases model
public function details()
{
return $this->hasMany(Detail::class, 'user_id');
}
$users = User::with('cases.details')->get();
foreach ($users as $user) {
// an user
foreach ($user->cases as case) {
// a case
foreach ($case->details as $detail) {
// the details of a case
}
}
}

Laravel - my scope method does not appear to be working

I am using scope to filter conditions for specific users, I have assigned a course to a teacher and when this teacher signs into their account, I only want this teacher to view their course, although my scope method doesn't appear to be working correctly. I am not getting an error, so I am not sure where I have gone wrong. I have added some of my code below, i would be very grateful for any help. Thanks
CoursesController index method;
public function index()
{
$courses = Course::ofTeacher()->get();
return view('admin.courses.index')->with('course', $courses); //pass data down to view
}
Course.php;
<?php
namespace App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Model;
class Course extends Model
{
protected $fillable = [
'id', 'title'
];
public function courses(){
return $this->belongsToMany('App\User');
}
public function teachers() {
return $this->belongsToMany(User::class, 'course_user')
}
public function scopeOfTeacher($query)
{
if (!Auth::user()->isAdmin()) {
return $query->whereHas('teachers', function($q) {
$q->where('user_id', Auth::user()->id);
});
}
return $query;
}
}
User.php;
public function isAdmin() {
return $this->role()->where('role_id', 1)->first();
}

Laravel relationship between two tables with two foreign keys

Hey how can I make relationships between two table.
Users: id, email
Notification: id, user_id(id of the logged user), client_id(id of sender)
I would like make relationship between users and notifications by user_id and client_id.
Then I will can get all notifications assigned to logged user, and get email of sender users.
I made that:
public function notifications_with_client() {
return $this->hasManyThrough('App\Models\User', 'App\Models\Notification', 'user_id', 'id', 'client_id');
}
But when I using query i got good notifications but with wrong email.
I got email from ralationship id(from users table) == id(from notifications table)
My query
$column = 'notifications_with_client';
$value[1] = ['email', 'notifications.id', 'client_id'];
$query->with([$column => function($query) use ($value) {
$query->select($value[1]);
}]);
Someone know what I do wrong?
You can try it by defining the following relations:
User Model
public function notifications()
{
return $this->hasMany('App\Models\Notification');
}
Notification Model
public function to()
{
return $this->belongsTo('App\Models\User', 'user_id');
}
public function from()
{
return $this->belongsTo('App\Models\User', 'client_id');
}
And then you can query it as:
$notifications = auth()->user()->notifications()->with('from')->get();
Or if you just want email then query it as:
$notifications = auth()->user()
->notifications()
->with(['from' => function($q) {
$q->select('email');
}])
->get();
public function user()
{
return $this->belongsTo(Users::class, 'user_id');
}
public function client()
{
return $this->belongsTo(Users::class, 'client_id');
}
With this code in your Notification Model you can get the logged user with
$this->user(); // $notification->user();
and the sender with
$this->client(); //$notification->client();
You cannot use $this->hasManyThrough(). It use for a different reason
You can use $this->belongsTo() like this.
class User extends BaseModel
{
public function user()
{
return $this->belongsTo(Notification::class, 'user_id');
}
public function client()
{
return $this->belongsTo(Notification::class, 'client_id');
}
}
Then you can query like.
User::with(['user']);
or
User::with(['client']);

Distant HasManyThrough

I have four Models:
User
Client
Store
Opportunity
The relationships are defined as such:
User hasMany Client
Client hasMany Store
Store hasMany Opportunity
User hasManyThrough Store, Client (this works)
The problem is that I'm attempting to access the User->Opportunity relationship via built-in Laravel relationships, but it doesn't seem as if I can do it without a custom Query or an additional user_id column on the opportunities table to allow direct access (even though one can be inferred from the Store->Client relationship). I'm also not a fan of nested foreach loops if they can be avoided.
My question:
Is there a way to go one level deeper and directly access a User's Opportunities in this scenario? The actual Model code and all relevant relationships are as follows:
User
class User extends Eloquent{
public function clients(){
return $this->hasMany('Client');
}
public function stores(){
return $this->hasManyThrough('Store', 'Client');
}
public function proposals(){
return $this->hasMany('Proposal');
}
public function opportunities(){ //This does the job, but I feel like it could be better
return Opportunity::join('stores', 'stores.id', '=', 'opportunities.store_id')->
join('clients', 'clients.id', '=', 'stores.client_id')->
join('users', 'users.id', '=', 'clients.user_id')->
select('opportunities.*')->
where('users.id', $this->id);
}
public function getOpportunitiesAttribute(){ //This just helps mimic the hasManyThrough shorthand
return $this->opportunities()->get();
}
}
Client
class Client extends Eloquent{
public function stores(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunities(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Store
class Store extends Eloquent {
public function client(){
return $this->belongsTo('Client');
}
public function opportunities(){
return $this->hasMany('Opportunity');
}
}
Opportunity
class Opportunity extends Eloquent {
public function store(){
return $this->belongsTo('Store');
}
}
I don't think there is such method in Laravel. You have to create your custom query. This custom query can be very expensive since multiple queries will be performed. Thus, the optimum solution for this, according to me, is to relate User and Opportunity with a foreign key.
However, if you don't desire to link User and Opportunity with a foreign key, then you can create a custom query to handle this. Simply add a "hasManyThrough" relation between Opportunity and Client model like,
<?php
class Client extends Eloquent{
public function store(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunity(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Then create a static function in User model.
<?php
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
public function client(){
return $this->hasMany('Client');
}
public function store(){
return $this->hasManyThrough('Store', 'Client');
}
public static function getOpportunityOfUser($userId)
{
$clients = User::find($userId)->client;
foreach ($clients as $client) {
$opportunities[] = Client::find($client->id)->opportunity;
}
return $opportunities;
}
}
Now you can access Opportunity realted to a User in one go like,
Route::get('/', function()
{
return $usersOpportunities = User::getOpportunityOfUser(1);
});
This will return all opportunity of all clients related to User with id '1'.
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function opportunities() {
return $this->hasManyDeep(Opportunity::class, [Client::class, Store::class]);
}
}

Laravel: hasMany() with take() only working on first result

I have the following models.
class User extends Eloquent {
public function comments() {
return $this->hasMany('Comment');
}
}
class Comment extends Eloquent {
public function user() {
return $this->belongsTo('User');
}
}
For the sake of this example, a user could have 1,000s of comments. I am trying to limit them to just the first 10. I have tried doing it in the User model via
class User extends Eloquent {
public function comments() {
return $this->hasMany('Comment')->take(10);
}
}
and via UserController via closures
$users = User::where('post_id', $post_id)->with([
'comments' => function($q) {
$q->take(10);
}
]);
Both methods seem to only work on the first record of the result. Is there a better way to handle this?

Categories