I got the following tables
actors
id
name
stats
id
name
actor_stat
actor_id
stat_id
quantity
I want, given an actor's name, take all the quantities associated.
Actor model:
class Actor extends Model
{
public function stats()
{
return $this->belongsToMany('App\Stat', 'actor_stat')->withPivot('quantity');
}
}
Stat model:
class Stat extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'actor_stat')->withPivot('quantity');
}
}
Query
public function request(){
$actors = Actor::where('name','Jack')->first();
$stat = $actors->pivot->quantity; //??
return response()->json(['actors' => $actors, 'stat' => $stat]);
}
Suggestions?
you can use eager loading to load related table like this:
$actors = Actor::with('stats')->where('name','Jack')->first();
//do loop foreach actor state then you can access to quantity column
foreach($actors->states as $state)
$state->pivot->quantity;
Related
Been having a hard time on figuring out how to implement updating records on a nested createMany based on the store method. I have these models and relations:
User Model:
class User extends Model
{
public function orders() {
return $this->hasMany(Order::class);
}
}
Order Model:
class Order extends Model
{
public function user() {
return $this->belongsTo(User::class);
}
public function subOrders() {
return $this->hasMany(SubOrder::class);
}
}
SubOrder Model:
class SubOrder extends Model
{
public function order() {
return $this->belongsTo(Order::class);
}
public function subOrderProducts() {
return $this->hasMany(SubOrderProducts::class);
}
}
SubOrderProducts Model:
class SubOrderProducts extends Model
{
public function subOrder() {
return $this->belongsTo(SubOrder::class);
}
}
Here are the tables:
orders table:
id name
sub_orders table:
id order_id date price
sub_order_products table:
id sub_orders_id product_id
Then on the store method:
public function store(StoreOrderRequest $request) {
$order = auth()->user()->orders()->create($request->validated());
$order_subs = $order->subOrders()->createMany($request->orders);
$order_sub_products = $request->only('subproducts');
foreach ($order_subs as $key => $value) {
$order_sub->subOrderProducts()->createMany($order_sub_products[$key]['products']);
}
}
What I wanted to achieve is to update or create those sub orders and sub order products. If the sub order is already existing, then update, otherwise create a new record with those corresponding sub order products.
Here's what I've tried so far:
public function store(UpdateOrderRequest $request, Order $order) {
$order->update($request->validated());
$order_subs = $order->subOrders()->updateMany($request->orders);
$order_sub_products = $request->only('subproducts');
foreach ($order_subs as $key => $value) {
$order_sub->subOrderProducts()->updateMany($order_sub_products[$key]['products']);
}
}
It's not working as expected since the order subs is only a single record. What's the best way or the right process for updating/creating these records? Thanks.
I'm new to Laravel and Eloquent.
In my app, I want to retrieve the current user's and other project members' tasks and show them in the view.
Here's the database relation I have made:
users (id)
projects (id, user_id)
user_project (id, user_id, project_id, role)
tasks (id, user_id, project_id)
(I have defined the required foreign keys in migrations)
To explain the relation, each project has a user (who has created the project). But in the user_project table, users can be assigned to other projects as well and their role is defined there. For example: There are two users:
id=1
id=2
And three projects:
id=1, user_id=1
id=2, user_id=2
id=3, user_id=1
user_project relationship:
id=1, user_id=1, project_id=1, role=admin
id=2, user_id=2, project_id=1, role=employee
id=3, user_id=2, project_id=2, role=admin
id=4, user_id=1, project_id=3, role=admin
And four tasks:
id=1, user_id=1, project_id=1
id=2, user_id=2, project_id=1
id=3, user_id=1, project_id=2
id=4, user_id=1, project_id=3
I want the user id=2 to be able to see tasks of project_id=1 because he is invited to that project as employee together with the tasks of project_id=2 because he has created that project. Of course the user shouldn't be able to see the tasks of project_id=3 because he's not a member. So, what's the neatest way to do this?
Here are the models I have defined:
class User extends Authenticatable
{
public function projects(){
return $this->hasMany(Project::class);
}
public function tasks(){
return $this->hasMany(Task::class);
}
public function joinedProjects()
{
return $this->hasMany(ProjectUser::class);
}
}
class Project extends Model
{
public function tasks(){
return $this->hasMany(Task::class);
}
public function users(){
return $this->hasMany(User::class);
}
}
class ProjectUser extends Model
{
}
class Task extends Model
{
public function projects(){
return $this->belongsTo(Project::class);
}
public function users(){
return $this->belongsTo(User::class);
}
}
Here's the way I'm trying to retrieve tasks of my project members (the ideal way I'm looking for to do this is: $tasks = $user->joinedProjects->tasks but I don't know how to do it, so here's how I'm currently trying to get this done):
class TasksController extends Controller
{
public function index()
{
$user = Auth()->user();
//I guess there are better ways to retrieve projects, but:
$projects = app('App\Http\Controllers\HomeController')->allProjects($user);
foreach($projects as $project){
$tasks[] = Task::where('project_id', $project->id);
}
return view('tasks.index', compact(['tasks','user']));
//gives error: Property [id] does not exist on the Eloquent builder instance
//when I'm trying to get $task->id in a foreach loop.
}
}
And here's the home controller (I need the allProjects() function in HomeController for some other class functions):
class HomeController extends Controller
{
function allProjects($user){
$projects = $user->projects;
$otherProjects = \App\ProjectUser::where('user_id',$user->id)->get();
foreach ($otherProjects as $project){
$projects[] = \App\Project::find($project->project_id);
}
return $projects;
}
}
First of all I think you should setup the joinedProjects as a many-to-many relationship instead, that way accessing it will feel more straightforward.
// in User model
public function joinedProjects()
{
// i'm assuming you want to always have access to the role property
return $this->belongsToMany(Project::class, 'user_project')->withPivot('role');
}
// in Project model
public function memberUsers()
{
return $this->belongsToMany(User::class, 'user_project')->withPivot('role');
}
With this relationship you should be able to call $user->joinedProjects to get the list of project that the user has joined.
To get the task, of course you can call the joinedProjects relationship and loop through the resulting project like the for loop you have set up. Or as an alternative you can make use of the collection class' pluck method.
class TasksController extends Controller
{
public function index()
{
// here I put load to eager load the project and task
$user = Auth()->user()->load('joinedProjects.tasks');
// OPTION 1: you can loop to get the tasks
$tasks = collect();
foreach($user->joinedProjects as $project){
$tasks = $tasks->merge($project->tasks);
}
// OPTION 2: or use pluck (must be eager loaded to work)
$tasks = $user->joinedProjects->pluck('tasks');
// $tasks should be unique here, but if it is not you can call the unique method of collection
$tasks = $tasks->unique('id');
return view('tasks.index', compact(['tasks','user']));
}
}
Your HomeController that you shared can also be simplified with the new relationship
class HomeController extends Controller
{
function allProjects($user){
// here i'm assuming user automatically joins a project when they create it
$projects = $user->joinedProjects;
return $projects;
}
}
Here's some additional reference to the unique method i use in the code and the lazy eager loading
I have three Models Record, Category and Subcategory. The Record table stores category_id and subcategory_id as foreign keys. There is a pivot table "category_subcategory".
I would like to retrieve all Records that have an "invalid Category Subcategory relation" in an elegant and performant way via a custom function in the Model.
What do I mean by "invalid Category Subcategory relations":
a) Record has as Categroy and Subcategory. But the Subcategory doesn't belong to the Category (No entry in pivot table)
b) Record has a Category but no Subcategory (subcategory_id = NULL). Because the Category itself has Subcategories, the subcategory_id of the Record should be NULL
c) Record has a Category and a Subcategory, but the Cateory itself has no Subcategories, therefore the Record should have the subcategory_id = NULL
With this custom function in the Model I would like to be able to do sth like this in a Controller:
Records::withInvalidCategorySubcategoryRelation()->get(); //or similar
rather than going through endless foreach loops in the Controller like
$records = Record::all();
foreach($records as record){ ...
Any suggestions are much appreciated!
Here are my Model Classes:
class Record extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function subcategory()
{
return $this->belongsTo(Subcategory::class);
}
}
class Category extends Model
{
public function subcategories()
{
return $this->belongsToMany(Subcategory::class);
}
}
class Subcategory extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Laravel offers whereNotExists that can be helpful here.
Add the following function to Record model:
// Adjust the func name as per your test ;)
public function scopeInvalidRecords($query)
{
return $query->whereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
AND
category_subcategory.subcategory_id = records.subcategory_id
');
});
}
In Controller:
Record::invalidRecords()->get();
// It'll give you all the invalid records(invalid as per your definition)
Hope it is helpful!
Thanks to #Zeshan Khattak I was able to get what I needed. Now I get the "invalid" Records where a) b) c) is met
public function scopeInvalidRecords($query)
{
return $query->whereExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
');
})->whereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
AND
category_subcategory.subcategory_id = records.subcategory_id
');
})->orWhereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
');
})->whereNotNull('subcategory_id');
}
I have a table called invoiceDetails that has item_id as foreign key from another table called items which has category_id as foreign key from table called categories.
I want to do that following using eloquent:
$result = InvoiceDetail::groupBy('item_id')
->selectRaw('sum(qty) as qty, item_id')->with('item', 'category')->get();
but I am getting error:
Call to undefined relationship [category] on model [App\InvoiceDetail].
Here's my relation inside Category model:
public function invoiceDetail() {
return $this->hasManyThrough('App\InvoiceDetail', 'App\Item', 'category_id', 'item_id');
}
Any suggestions?
Not sure you would even need a hasManyThrough relation here, unless you want to fetch all InvoiceDatail objects belonging to all items which in turn belong to the Category. That part is not clear from your question.
But in your example you are fetching items with their category from distinct item_id.
The reason this is not working is because you are trying to fetch the category relation from the InvoiceDetail object, which does not exist.
->with('item', 'category')
You want to load the Category based on the item relation, not based on the InvoiceDetail, try the dot notation (given that you did define the other relations)
->with('item.category')
Relations should be like this:
class InvoiceDetail extends Model
{
public function item()
{
return $this->belongsTo(\App\Item::class);
}
}
class Item extends Model
{
public function invoiceDetails()
{
return $this->hasMany(\App\InvoiceDetail::class);
}
public function category()
{
return $this->belongsTo(\App\Category::class);
}
}
class Category extends Model
{
public function items()
{
return $this->hasMany(\App\Item::class);
}
public function invoiceDetails()
{
return $this->hasManyThrough(\App\InvoiceDetail::class, \App\Item::class, 'category_id', 'item_id');
}
}
You would want to use the hasManyThrough if, for example, you have a Category and you want to load all the InvoiceDetails directly.
dd($category->invoiceDetails);
I want to return the sum of "amount" from my payments table. There can be many payments for one invoice. The below "->sum('amount') does not work, it returns:
Call to a member function addEagerConstraints() on a non-object.
How to return the sum of all payments for each invoice in my relation?
Invoices Model:
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments')->sum('amount');
}
}
Expenses Model:
class Payments extends Eloquent {
public function invoices()
{
return $this->belongsTo('Invoices');
}
}
My table "payments" holds the foreign key of my tables invoices, which is invoices_id.
Starting by Laravel 8 you can simply use withSum() function.
use App\Models\Post;
$posts = Post::withSum('comments', 'votes')->get();
foreach ($posts as $post) {
echo $post->comments_sum_votes;
}
https://laravel.com/docs/8.x/eloquent-relationships#other-aggregate-functions
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments');
}
}
class Payments extends Eloquent {
public function invoices()
{
return $this->belongsTo('Invoices');
}
}
In your controller
Invoice::with(['payments' => function($query){
$query->sum('amount');
}])->get();
;
You can show this package
$invoices = Invoices::withSum('payments:amount')->get();
First decide which Invoice (for example id 1)
$invoice = Invoices::find(1);
Then eager load all the corresponding payments
$eagerload = $invoice->payments;
Finally assuming you have the amount field in your Invoice model you can simply find the sum using the method below:
$totalsum = $eagerload->sum('amount');
This is also possible. we can do by model itself.
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments')
->selectRaw('SUM(payments.amount) as payment_amount')
->groupBy('id'); // as per our requirements.
}
}
}
Note
SUM(payments.amount)
payments is tableName
amount is fieldName
I found a simple way to acomplish this in here, you can use withPivot() method.
You can redefine a bit your relation to something like following
public function expenses()
{
return $this->belongsToMany('Expenses', 'invoices_expenses')
->withPivot('name', 'amount', 'date');
}