Laravel createMany on createMany while preserving relation - laravel

I have the same issue as this question asked. However, I would like to insert the data on the nested createMany with it's relation, and not insert multiple entries like:
id question_id answer
1 1 Good
2 1 Meh
3 2 Good
4 2 Meh
So if the question_id is 1 and the answer on that question is Good and for question_id 2, the answer on that question is Meh so that result should be:
id question_id answer
1 1 Good
2 2 Meh
Basically what I have is:
A User hasMany orders
An Order hasMany sub orders
A SubOrder hasMany sub order products
Here are the 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:
$order = auth()->user()->orders()->create($request->validated());
$order_subs = $order->subOrders()->createMany($request->orders);
foreach ($order_subs as $order_sub) {
$order_sub->subOrderProducts()->createMany($request->orders);
}
The $request->orders data is:
{
"name": "My Order",
"orders": [
{
"date": "2022-05-17",
"product_id": [1],
"price": 1
},
{
"start_date": "2022-05-18",
"product_id": [2],
"price": 2
}
]
}
This means that the order has two sub orders, with their corresponding products. I end up getting:
orders table:
id name
1 Super Order
sub_orders table:
id order_id date price
1 1 2022-05-17 1
2 1 2022-05-18 2
sub_order_products table:
id sub_orders_id product_id
1 1 1
2 1 2
3 2 1
4 2 2
My desired output would be to preserve the relation on the sub_order_products table, save and match the correct IDs. That would be:
sub_order_products table:
id sub_orders_id product_id
1 1 1
2 2 2
I'm thinking to change request->orders data and have some IDs for the sub_orders_id or maybe nest the createMany methods but I can't figure out how to. How to achieve this? Thanks!

Related

How to establish a dynamic model relationship in Laravel?

I need to establish a dynamic relationship but I can't.
The table design is as follows.
Page Table
id
title
1
Hello world
2
Contact
Category Table
id
title
1
Electronics
2
Sports
Blog Table
id
title
1
First blog
Links Type
id
name
1
Page
2
Category
3
Blog
Links Table
id
type_id
relation_id
slug
1
1
1
page/hello-world
2
1
2
page/contact
3
2
1
category/electronics
3
2
2
category/sports
3
3
1
blog/first-blog
CODE
Controller:
$links = Links::with('title')->get();
return response()->json($links);
// I need to get the "title" key.
Links Model:
public function title() {
switch($this->type_id) {
case '1':
return $this->hasOne(Page::class, 'id', 'relation_id');
break;
case '2':
return $this->hasOne(Category::class, 'id', 'relation_id');
break;
case '3':
return $this->hasOne(Blog::class, 'id', 'relation_id');
break;
}
}
This state does not work because the model has not yet been formed.
Error Output
Call to a member function addEagerConstraints() on null
How should I go about it?
Thanks.
What you are doing can be achieved by using polymorphic relationships.
pages
id - integer
name - string
categories
id - integer
title - string
blogs
id - integer
title - string
link_types
id - integer
name - string
links
id - integer
link_type_id - integer
linkable_id - integer
linkable_type - string
Page, Category and Blog model should define these relationships
public function link_types()
{
return $this->morphToMany(LinkType::class, 'linkable', 'links')->using(Link::class);
}
public function links()
{
return $this->morphMany(Link::class, 'linkable')
}
LinkType model should define these relationships
public function pages()
{
return $this->morphedByMany(Page::class, 'linkable', 'links')->using(Link::class);
}
public function categories()
{
return $this->morphedByMany(Category::class, 'linkable', 'links')->using(Link::class);
}
public function blogs()
{
return $this->morphedByMany(Blog::class, 'linkable', 'links')->using(Link::class);
}
Link model should define these relationships
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Link extends MorphPivot
{
/**
* Get the parent linkable model (blog, category, page).
*/
public function linkable()
{
return $this->morphTo();
}
public function link_type()
{
return $this->belongsTo(LinkType::class, 'link_type_id');
}
}
Eloquent Relationships - One To Many Polymorphic Relations
Eloquent Relationships - Many To Many Polymorphic Relations
$link = Link::with('linkable')->first();
$link->linkable->title;

How to do multiple aggregations in a "has through" relationship in a single query using Laravel?

I have the following tables:
Venues
id
Offers
id
venue_id
Orders
id
offer_id
quantity
Venues can have many offers, and offers can have many orders. This is all modelled in the appropriate Eloquent models like so:
class Venue extends Model
{
public function offers()
{
return $this->hasMany(Offer::class);
}
}
class Offer extends Model
{
public function orders()
{
return $this->hasMany(Order::class);
}
}
I want to run a single query which allows me to see every venue and the number of orders it has. e.g.
venue_id | number_of_orders
---------------------------
5 | 20
15 | 0
8 | 123
I was easily able to do this using raw SQL like so:
select venues.id, sum(offersGrouped.number_of_orders) as total_number_of_orders
from venues
left outer join (
select offers.id, offers.venue_id, sum(orders.quantity) as number_of_orders
from offers
left outer join orders on offers.id = orders.offer_id
group by offers.id
) offersGrouped on venues.id = offersGrouped.venue_id
group by venues.id;
However, how can I do the same thing using Eloquent?
I am using Laravel 5.1, but I don't mind if answers uses features from newer versions of Laravel if required.
Add a HasManyThrough relationship to your Venue model:
public function orders()
{
return $this->hasManyThrough(Order::class, Offer::class);
}
Then you can do this:
$venues = Venue::with('orders')->get();
foreach($venues as $venue) {
// $venue->orders->sum('quantity')
}
Or an in-database solution:
public function orders()
{
return $this->hasManyThrough(Order::class, Offer::class)
->groupBy('venue_id')->selectRaw('SUM(quantity) sum');
}
$venues = Venue::with('orders')->get();
foreach($venues as $venue) {
if($venue->orders->isNotEmpty()) {
// $venue->orders->first()->sum
} else {
// 0
}
}

Defining relations on same table Laravel 5.3

I have a refferal system in my application.ie, One user can reffer other.
Users table
id name
1 A
2 B
3 C
Referrals table
id referrer_id referee_id
1 1 2
2 2 3
I want to show the referrals made by a user.
User Model
public function referrals()
{
return $this->hasMany('App\Models\Referral','referee_id','id');
}
Referral Model
public function referrer_user()
{
return $this->belongsTo('Modules\User\Models\User','referrer_id','id');
}
public function referee_user()
{
return $this->belongsTo('Modules\User\Models\User','referee_id','id');
}
Defining hasMany relationship in User model and belongsTo relationship in Referrals Model, I'am not getting the desired result.
#foreach($user->referrals as $key => $referral)
{{dd($referral->referee_user->name)}}
#endforeach
This returning me the name of user itself(referrer) not the referee
You are referring the ManyToMany relationship the wrong way.
Anyways, it'll be much easier and efficient the following way.
id name referred_by
1 abc null
2 def 1
3 gfd 2
User Model
/**
* Who did he bring onboard?
*/
public function referrals(){
return $this->hasMany('....User', 'referred_by');
}
//Who brought him onboard..
public function referree(){
return $this->belongsTo('...User', 'referred_by');
}
Just check out the appropriate syntax.

How to join tables in laravel 5.4

In my case, I have 3 tables like Question, options, and answers
Questions table
|id | question_name|
------------------------------
1 question1
2 question2
3 question3
options table
id | question_id | options |
----------------------------------------
1 1 option1
----------------------------------------
1 1 option2
----------------------------------------
1 1 option3
----------------------------------------
1 1 option4
Answers table
id | customer_id | question_id | answer(selected by user) |
--------------------------------------------------------------------
1 1 1 option1
--------------------------------------------------------------------
1 2 2 option2
--------------------------------------------------------------------
1 1 3 option3
--------------------------------------------------------------------
1 1 3 option2
How can I get below output from answers using joins table
For customer 1
question1
--option1
question2
--option2
question3
--option3
--option2
I have eloquent relation,
Question model
class Question extends Model
{
public function options()
{
return $this->hasMany(Option::class);
}
public function customer()
{
return $this->belongsTo(CustomerProfile::class);
}
public function answers()
{
return $this->hasMany(Answer::class);
}
}
Option model
public function question()
{
return $this->belongsTo(Question::class);
}
Answer model
public function customer()
{
return $this->belongsTo(CustomerProfile::class);
}
public function question()
{
return $this->belongsTo(Question::class);
}
That is how my relationships looks like, Now I just need to join the tables to get output.
In order to get the related options for your questions, you can use eager loading.
An example would be:
$questions = Question::with('options')->get();
You will then have a collection with the questions, and their related options. You will need to construct a loop to get the data you want.
Going off the comment you left on Aaron Fahey's answer, you'll need to add a constraint to the query and the eager-load:
$customerId = 1;
$questions = Question::with([
'options', 'answers' => function ($query) use ($customerId) {
$query->where('customer_id', $customerId);
}])
->whereHas('answers', function ($query) use ($customerId) {
$query->where('customer_id', $customerId);
})
->get();
https://laravel.com/docs/5.4/eloquent-relationships#constraining-eager-loads
https://laravel.com/docs/5.4/eloquent-relationships#querying-relationship-existence
Hope this helps!
I guess, you also have defined eloquent relations in Customer Model class.
If you do so, then you can retrieve all the answers of a particular customer through customer model and this will give you the hook to get the answer's question and its all options:
$customer = Customer::find($customerId);
$answer = $customer->answers()->where('id', $answerId)->get();
$question = $answer->question;
$questionOptions = $question->options;
I hope it will help.

How to do complicated query SQL in Laravel?

I use three entities: user, categories, user_categories, articles, articeles_category
Each user can be subscribe on some categories, so table user_categories will be:
user_category
id user_id category_id
1 1 1
articles_category
id article_id category_id
1 1 1
articles
id name
1 Bla
So, I need to select all articles where articeles_category that correspond to the categories on user subsribed in table user_categories.
I tried this:
Articles::with("categories")->with("publisher.categories")->where("user_id", 1);
public function categories()
{
return $this->hasMany("App\ArticlesCategory", "article_id", "id");
}
public function publisher()
{
return $this->hasOne("App\User", "id", "user_id");
}
I have some ideas.
At first join tables:
user_categories with articeles_category by conditions:
user_categories.category_id = articeles_category.id AND user_categories.user_id = 4
Second is:
Select articles from Articles join with previous query.
Third is
To make global where by user_id
If your models are linked from users to categories to articles, you can do:
$categories = $user->categories()->with('articles')->get();
Then group them under the same collection (if you want):
$articles = $categories->map(function($category){
return $category->articles;
});
Alternatively, if you do not have the user instance:
$user = User::find($user_id)->with('categories.articles').->get();
See eager loading: https://laravel.com/docs/5.4/eloquent-relationships#eager-loading

Resources