PHP Laravel 5.2: Eloquent relationship for subscriptions - php

I think I got a little stuck and I just need someone to clarify things. So what I got is a user system which includes subscriptions for people to "subscribe" to their content (as you already know it from FB, Twitter, YT etc).
My database model looks like this:
Users
id
username
Subsccriptions
id
user_id
sub_id
Currently I have one model for Users and one Model for Subscriptions. The model from the user:
public function subscriptions()
{
return $this->hasMany('App\Subscription');
}
In comparison, my subscription object:
public function user()
{
return $this->belongsTo('App\User');
}
So in my personal opinion this is a 1:many relationship. If I call the user object with User->subscriptions()->get() I can get the subscription and it's sub id, so I know who THE CURRENT user has subscribed.
People told me the opposite and it's supposed to be a many-to-many relationship. Did I do something wrong? Can I automatically convert the sub_id into a user through relationships in Eloquent?
// EDIT:
Here is the current code to receive my subscribers for a user
public function index()
{
$subs = Auth::user()->subscriptions()->get()->all();
$submodels = [];
foreach($subs as $sub) {
array_push($submodels,User::find($sub->sub_id));
}
return view('home', [
'subscriptions' => $submodels
]);
}
}

Related

How to fetch customer information through an eloquent model

I have three models: Payment, Booking and Customer. A payment relation belongs to the Booking model and the Booking model belongs to the Customer model.
I need to make a customer relation inside the Payment model, however, they are not related, the only relation between them is through the Booking model.
My code:
public function booking(){
return $this->belongsTo(Booking::class , 'payable_id' , 'id')->with('customer');
}
// what i need , i need to consume that booking function to get customer info
// something like this
public function customer(){
// consume the booking relation
// return customer info
}
Could you please advise me on the right approach?
The answer to that is a Has One Through or Has Many Through relationship, depending on your business model.
Class Payment {
public function customer()
{
return $this->hasOneThrough('Customer', 'Booking');
}
// or:
public function customers()
{
return $this->hasManyThrough('Customer', 'Booking');
}
}
Hope it helps.
Based on your question you want to get customer information through "booking" let's say it is your lookup table where a customer id and payment id fields exist.
It should look like this
ERD of table image
In your Booking Model you have should two functions
public function customer() {
return $this->belongsTo(Customer::class, 'cust_id', 'id');
}
public function payment() {
return $this->belongsTo(Payment::class, 'payable_id', 'id');
}
In both your Customer and Payment Models add
public function bookings() {
return $this->hasMany(Booking::class, '(cust_id or payable_id respectively)', 'id');
}
in case you want to access booking via customer/payment models as well.
Now with those relationships you can access both payment and customer by accessing booking.
e.g.
$customers = Booking::all()->with('customer');
$payments = Booking::all()->with('payment');
Now you can manipulate all other data for display and/or editing or whatever you need to do with it.
You can also now access a customer's data with all of his/her bookings and payments associated. Below code results a collection of payments based on c
$customer = Customer::find(1)->with('bookings')->first();
$payments = $customer->bookings->payment;
For more questions feel free to delve in to the Official Laravel documentations.
In answering your question I based it on this.
https://laravel.com/docs/5.8/eloquent-relationships
Hope I helped and cheers!

Using eloquent relation

I am trying to understand how to effectively use Eloquent relationships to have some high level functions in the model.
I have a subscription app with 2 tables, 'users' and 'subscriptions'.
This is a legacy system so I cannot just change things in any way I want.
Table users (model App\User)
id
email
active (0/1)
join_date
address etc
phone
Table subscriptions (model App\Subscription)
id
user_id
box_id (what the person is subscribed to get)
amount
Users are marked active or not active.
I would like to have a static method on the Subscription model that will give me all the active subscriptions. This data is then fed into other parts of the application.
This is derived by joining subscriptions to users and filtering based on the active column.
The query is like this:
SELECT users.*, subscriptions.*
FROM subscriptions
JOIN users ON users.id = subscriptions.user_id
WHERE users.active = 1
Subscription model
class Subscription extends Model
{
public static function allActive()
{
// This works except it doesn't use the eloquent relationship
return static::where('users.active', 1)
->join('users', 'users.id', '=', 'subscriptions.user_id')
->select('users.*','subscriptions.*')
->get();
}
public function user()
{
return $this->belongsTo(User::class);
}
}
User model
class User extends Authenticatable
{
use Notifiable;
public function subscriptions()
{
return $this->hasMany(Subscription::class);
}
}
I would use it like this:
$subscriptions = \App\Subscription::allActive()->toArray();
print_r($subscriptions);
I have 2 questions.
How do I rewrite the allActive function to use the relationship I already defined? Any solution should generate SQL with a JOIN.
In the returned data, how do I separate the columns from the two separate tables so that it is clear which table the data came from?
Given the relationships you have wired up, to get only active subscriptions from the model class you will have to do it this way:
class Subscription extends Model
{
public static function allActive()
{
$activeSubcriptions = Subscription::whereHas('user', function($query){
$query->where('active', 1) //or you could use true in place of 1
})->get();
return $activeSubcriptions;
}
public function user()
{
return $this->belongsTo(User::class);
}
}
Thats working with closures in Laravel, quite an efficient way of writing advanced eloquent queries.
In the callback function you will do pretty much anything with the $query object, its basically working on the User model since you mentioned it as the first parameter of the ->whereHas
Note that that variable has to have EXACTLY the same name used in declaring the relationship
The above i suppose answers your first question, however its highly recommended that you do most of this logic in a controller file
To answer question 2, when you execute that get() it will return Subscription objects array so to access the info based on columns you will have to go like:
$subscriptions = \App\Subscription::allActive();
foreach($subscriptions as $subscription){
$amount = $subscription->amount; //this you access directly since we working with the subscription object
$box_id = $subscription->box_id;
//when accessing user columns
$email = $subscription->user->email; //you will have to access it via the relationship you created
$address = $subscription->user->address;
}

query results of signed in user in laravel

I currently have a ticket system built and I would like users to view the status of their Open and Closed tickets.
I can query the database and get all tickets to display, but I only want the logged in users tickets to show. How would I achieve this. Been at it a few days now and I'm at a loss.
What I have:
ROUTE (This returns a 404 due to the {ticketId}) / Note: If I remove the {ticketId} from the Route I get the Type error: *Too few arguments to function App\Http\Controllers\TicketsController::openTickets(), 0 passed and exactly 1 expected"*
Route::get('/tickets/open-tickets/{ticketId}','TicketsController#openTickets')->name('open-tickets');
TICKETS CONTROLLER
public function openTickets($ticketId){
$tickets=Classified::find($ticketId);
$ticketId = Auth::user('id');
$tickets = DB::table('tickets')->orderBy('st_id', 'DESC')->where('status', '=', 'OPEN')->where('user_id', '=', $id)->paginate(4);
return view('tickets.open-tickets',compact('tickets'));
Ticket MODEL
class Ticket extends Model
{
use Notifiable, HasPermissionsTrait;
protected $ticketId = 'st_id';
protected $guarded=[];
protected $dispatchesEvents = [
'created' => Events\NewTicket::class
];
}
WORKING QUERY TO DISPLAY ALL TICKETS
public function openTickets(){
$tickets = DB::table('tickets')->orderBy('st_id', 'DESC')->where('status', '=', 'OPEN')->paginate(4);
return view('tickets.open-tickets',compact('tickets'));
I should mention that I changed my id to st_id on the tickets table as I'm using the ID to display the ticket number like this 201804001
Again I'm just looking for a solution to have the logged in user view their tickets and NOT all tickets in the database.
Image of Tickets Table:
You already have a Ticket model but you're using the DB facade to query the tickets table.
Instead, associate Tickets with Users via a one-to-many relationship.
class Ticket extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
In your User class:
public function tickets()
{
return $this->hasMany(Ticket::class);
}
Now you can do $user->tickets()->where('status', 'OPEN')->get(); to get all the open tickets for that user.
Even further, you can add a scope to the Ticket model.
In your Ticket class:
public function scopeOpen($query)
{
return $query->where('status', 'OPEN');
}
This will allow you to make your queries more fluent:
$user->tickets()->open()->get();
To associate a ticket with a user, simply do something along the following lines:
$user->tickets()->save(new Ticket([
'status' => 'open',
'title' => 'Problem logging in',
'body' => 'Lorem ipsum dolor sit amet'
]));
For more information about Eloquent relationships (specifically one-to-many relationships), see the documentation here.
To list all of a user's open tickets, use the following route:
Route::get('tickets/open-tickets/', 'TicketsController#index');
To show an individual ticket, you'll want the following route:
Route::get('tickets/open-tickets/{ticket}', 'TicketsController#show')
Use the following methods in your TicketsController:
public function index()
{
// user() is a helper for Auth::user()
$tickets = user()->tickets()->open()->paginate(4);
return view('tickets.open-tickets', compact('tickets'));
}
public function show(Ticket $ticket)
{
// If the ticket does not belong to the logged in user, abort
if ($ticket->user_id != user()->id) {
abort(403, 'This is not your ticket');
}
return view('tickets.show', compact('ticket'));
}
You'll notice that the show method in your controller type-hints the Ticket model. This is called route-model binding, which is very useful and you can read more about that here.
It may seem like I'm overcomplicating things compared to your original code, but trust me, it's better to understand these things early on! They'll help you a whole lot during development.

Laravel - How to chain Eloquent relationship - Eager Loading

I'm using Laravel 5.4, Laravel Roles from here and Eloquent relationships.
I'm trying to retrieve the users of a company along with their roles.
User::find(1)->roles gets me the user's roles whose id =1
Company::find(1)->users gets me all the users that belongs to the company whose id =1 (without the roles of users).
Company::find(1)->users->roles returns an error Property [roles] does not exist on this collection instance.
Questions
Is it possible to do what I want to do ?
If so, how should I do it ?
User.php
class User extends Authenticatable
{
use HasRoleAndPermission;
public function company()
{
return $this->belongsTo('App\Company');
}
public function user()
{
return $this->belongsTo(User::class);
}
}
HasRoleAndPermission.php
trait HasRoleAndPermission
{
public function roles()
{
return $this->belongsToMany(config('roles.models.role'));
}
}
Company.php
class Company extends Model
{
public function users() {
return $this->hasMany('App\User');
}
}
$company = Company::with('users.roles')->find(1);
Will load all the users, and all the roles for each user.
Update
According to your comment, you don't want the company data. Just users and roles Using eager loading and relationship existence.
$users = User::with('roles')
->whereHas('company' => function($query){
$query->where('name', '=', 'company'); //If you don't have the company ID
})->get();
Without relationship existence
$users = User::where('company_id', 1)->with('roles')->get();
1 company has many users.
1 users has many roles.
You are trying to get the roles of a collection of users (the property only exists for one user) thus, the property doesn't exists for the collection.
If you want to get all the roles of all users in the company, you might try the above code:
$roles = [];
Company::find(1)->users->foreach(function($user) {
$roles = array_merge($roles, $user->roles);
});
--------- edit ---------
For eager loading the roles of users, you must use with, as suggested by #Ohgodwhy, but I'd refactor a little:
$users = User::with('roles')->where('company_id', $companyId)->get();
Now you have the array of users eager loading their roles. You still can't access directly $users->roles, you must first get a user, only then get its roles.

Getting Model from within Collection

Something very basic but I'm having a hard time solving this.
I have a list of users in the database that show as online users. I am fetching these users by their user_id
Model
public function scopeloggedInUser($query){
return $query->select('user_id')->get();
}
when I var_dump or dd it shows that its a collection of a list of currently logged in users. (Said it was super simple).
I need to fetch those individual users. How do I dilute this to the individual user within the Online Model.
Within the Controller
public function index(Online $online)
{
$activeuser = $online->loggedInUser();
return view('user.user', compact('activeuser'));
}
In your online-model specify a relationship to the real user like this:
public function user()
{
return $this->hasOne('App\User');
}
In your view you can now access each user in your foreach-loop like this:
foreach ($activeusers as $user)
{
echo $user->user->username; // or whatever fields you need
}
But to be honest: in your case I wouldn't set up a new database table and new model if you need this functionality.
Move your logic to your User model and add a boolean field to your user table and change your query-scope to this (again: in your user model)
public function scopeOnline($query){
return $query->where('online', 1);
}
You also shouldn't do a get() within a scope because then you have no more access to the query builder. For example: you want all logged in users that are female.
With get: not pretty.
Without get:
User::online()->where('gender', '=', 'female')->get();

Categories