after 2 days of looking on laravel docs and stackoverflow with no success I have to ask the question
I have 4 models so far and I am trying to get all related info in one go.
user model has relationship:
public function transactions() {
return $this->hasMany( 'App\Transaction' );
}
store model has relationship: :
public function transactions() {
return $this->hasMany( 'App\Transaction' );
}
product model has relationship: :
public function transactions() {
return $this->hasMany( 'App\Transaction' );
}
and finally transaction model has relationships:
public function user() {
return $this->belongsToMany( 'App\User' );
}
public function product() {
return $this->belongsToMany( 'App\Product' );
}
public function store() {
return $this->belongsToMany( 'App\Store' );
}
transaction table (looks like a pivot table):
transactions:
id - integer
user_id : integer
product_id : integer
store_id : integer
....... some additional columns containing transaction specific data.
I would like to get all transactions with referenced details for auth user
aka "$user->transactions" however with this i am getting only ids no data from related tables. I did try by joining but it looks messy and I think there is better way of getting this data well structured in laravel
the join looks like:
$transactions = DB::table( 'transactions as t' )->where( 'user_id', Auth::id() )
->join( 'products', 'products.id', '=', 't.product_id' )
->join( 'stores', 'stores.id', '=', 't.store_id' )
->get( array( 't.id', 't.product_id', 'products.name as product_name', 't.store_id', 'stores.name as store_name' ) );
my objective is to get user object with all information about user including transactions and other details as one JSON response using as little queries as possible.
You just have to append the needed values from your relationships on the transaction. First add them as attributes on the Transaction model as follows:
public function getProductNameAttribute()
{
return $this->product?$this->product->name:"";
}
public function getStoreNameAttribute()
{
return $this->store?$this->store->name:"";
}
Now, you can append them to your transaction in the Transaction model like this:
protected $appends = ['product_name','store_name'];
And retrieve the transactions as usual like this:
Auth::user()->transactions;
Or append them to the transactions (after the first step) at run time like this:
Auth::user()->transactions()->setAppends(['product_name','store_name'])->toArray();
If your objective is to get an user object then you should query the user, not the transactions.
Since you have defined all the relationships you can leverage the query to Eloquent, no need to do it by hand.
User::whereId(Auth::user()->id)->with('transactions','transactions.product','transactions.store')->get();
Related
Have researched extensively eloquent relationships and laravel all afternoon and can't seem to find a solution.
I have a transaction table displaying User IDs in the From and To columns. I want to show the users emails that correspond to their IDs.
Here is my relationships:
Transaction model:
public function user()
{
return $this->belongsTo('App\User', 'id', 'email');
}
User model:
public function transaction()
{
return $this->hasMany(App\Transaction);
}
And heres the code from the controller. However I KNOW this just returns ALL the transactions. But I really can't find how I would display the emails instead of the IDs:
public function index()
{
$table = Table::create(Transaction::get());
return view('table', compact('table'));
}
Maybe you should try to get transactions with related model (user) or with join to users table?
$transactions = Transaction::query()->with(['user'])->get();
$table = Table::create(Transaction::get());
$table->addColumn('email', 'E-mail', function($model) {
return $model->user->email;
});
return view('table', compact('table'));
I have two tables: users, orders. I try to get all orders for current user.
Users Orders
_____ ______
id | name id | user_id
User model:
public function orders(){
return $this->hasMany("App\Order");
}
Order model:
public function user(){
return $this->hasOne("App\User", 'user_id', 'id');
}
Query in controller:
public function index()
{
$orders = Order::where('user_id', Auth::guard('api')->id())->get();
return response()->json(
$orders->user
);
}
I get NULL result, I do something wrong, because there are related rows in both tables.
If you want to retrieve all the Orders belonging to the current user, try using the following function.
public function index()
{
$orders = Auth::user()->with('Orders')->get()->toArray();//To get the output in array
/* ^ ^
This will get the user | This will get all the Orders related to the user*/
return response()->json($orders);
}
As pointed out by #Martin Heralecký, you would also need to change the hasOne() to belongsTo() in Order Model. See following (copied from #Martin Heralecký answer)
public function user(){
return $this->belongsTo("App\User");// second and third arguments are unnecessary.
}
Why belongsTo():
has_one and belongs_to generally are the same in the sense that they point to the other related model. belongs_to make sure that this model has the foreign_key defined. has_one makes sure that the other model has_foreign key defined.
Your $orders array will look something like this:
User => [
id => 'user id',
name => 'user name'
orders => [
0 => [
//order data
]
1 => [
//order data
]
.
.
.
.
]
]
In Order model you need to use the belongsTo relationship:
public function user()
{
return $this->belongsTo("App\User"); // second and third arguments are unnecessary.
}
In User model you can use hasMany relationship, for example in:
App/User.php
Add
public function orders()
{
return $this->hasMany("App\Order", "user_id", "id");
}
Now you can use this:
return User::find(1)->orders;
I have 3 models
User
id
Event
id
end_time
EventUser (this represents a user who's joined an event with a comment)
user_id
event_id
comment
We want to do $user->events
(to list the event details for all events user has joined)
But, as you can see, the intermediate table is a pivot table... therefore the events table does not reference it (which is the fourth parameter of the hasManyThrough function).
For example, I may want to get event details for all events a user has joined that has ended.
For now I am doing this in the User model
private function eventQuery() {
return Event::join('event_user', 'events.id', '=', 'event_user.event_id')
->where('user_id', $this->id); // event_user.user_id
}
public function events() {
return $this->eventQuery()->get();
}
public function pastEvents() {
return $this->eventQuery()
->where('end_time', '<', new \DateTime()) // events.end_time
->get();
}
But is there a laravel way to do it like the hasManyThrough in this situation?
The laravel way is:
User Model:
public function events()
{
return $this->belongsToMany('App\Event')->withPivot('comment');
}
Event Model:
public function users()
{
return $this->belongsToMany('App\User')->withPivot('comment');
}
When you do a call to:
$user = App\User::whereId($id)->where('end_time','<=',$now)->with('events')->get();
The events relation will be retrieved with the comment field of the pivot table, as you want it to be.
I want to perform the following (with some rather crude pseudo code):
SELECT a users orderLines WHERE the orderheader's status ='paid' AND the orderLine's productId>5
In other words, a user can place many orders. Each order has one or many order lines. I want to find all of the order lines that the user has placed (order lines, not orders) but only if the order header has a certain status, and only if the order line has another parameter checked. This could be the date the line was added, or the productId being x, and so on.
Simple enough to do with a standard MySql query.
I have the nescessary models:
User
OrderHeader (Intermediate relationship)
OrderLine (Distant relationship - this is what I want to fetch, via the intermediate)
Here are how the relationships are defined in each model:
User
public function orders()
{
return $this->hasMany('App\OrderHeader', 'user_id', 'id');
}
public function lines()
{
return $this->hasManyThrough('\App\OrderLine', 'App\OrderHeader', 'user_id', 'order_header_id');
}
OrderHeader
public function lines()
{
return $this->hasMany('App\OrderLine', 'order_header_id', 'id');
}
public function user(){
return $this->belongsTo('User', 'id', 'user_id');
}
OrderLine (Fetch these for the User, using hasManyThrough)
public function header()
{
return $this->belongsTo('App\OrderHeader', 'order_header_id');
}
public function products()
{
return $this->belongsToMany('App\Product');
}
So, I load the User, using:
$person = User::findOrFail($id)
Then I can use:
$user->lines()->where('product_id','>=',10)->paginate(20);
So, that works brilliantly to get ALL of the lines that the user has placed, which match the condition on the line records. However, I can't figure out how to add a second condition on the intermediate, so that not only do I check the product_id, but also interrogate the OrderHeader entity via the orders() relationship.
I've tried:
return $user->orders()->where('status','=','Paid')->lines()->where('product_id','>=',20))->paginate(20);
but that returns the error: Call to undefined method Illuminate\Database\Query\Builder::lines()
hasManyThrough is a special case in Eloquent, where table is joined (intermediate table), so it's pretty simple - just query that table. Nothing to do with the other relation.
This is what you want:
$throughTable = $user->lines()->getParent()->getTable();
$user->lines()
->where('product_id', '>=', 10)
->where('orderheaders.status', 'paid')
// or
// where("{$throughTable}.status', 'paid')
->paginate(20);
Btw this relation is wrong:
// OrderHeader model
public function user(){
return $this->belongsTo('User', 'id', 'user_id'); // wrong keys order
}
// should be
public function user(){
return $this->belongsTo('User', 'user_id', 'id');
}
I'm just getting started with Laravel so please forgive any noobness.
I have a User and Order model, a user has many orders:
# Inside User model
public function orders()
{
$this->hasMany('Order');
}
# Inside Order
public function user()
{
return $this->belongsTo('User');
}
// Not sure if this is upsetting anything (also in Order)
public function products()
{
return $this->belongsToMany('Product');
}
So I think I have the above right.
But when I do this:
$users = User::with('orders')->find(1);
return $users;
I get Call to a member function addEagerConstraints() on null.
However, if I do it the other way around, it works great:
$orders = Order::with('User')->get();
return $orders;
What am I doing wrong / what don't I understand?! Or is my problem bigger than I think?
Database:
The problem is you don't have return for your orders relationship. It should be:
public function orders(){
return $this->hasMany('Order');
}
You should also use your relationships case sensitive. you showed:
$orders = Order::with('User')->get();
is working, but you should rather use
$orders = Order::with('user')->get();
to avoid extra queries to your database in future
For anyone else that runs across this, I was having the same issue, but my problem was that I had the foreign/local keys swapped. Example:
// This is correct for hasX relationships
public function user() {
return $this->hasOne('App\Models\User', 'user_id', 'local_key_user_id');
}
// This is correct for belongsTo relationships
public function user() {
return $this->belongsTo('App\Models\User', 'local_key_user_id', 'user_id');
}
Notice that for hasX relationships, the foreign key is the second parameter, and the local key is the third. However, for belongsTo relationships, these two are swapped.
Probably doesn't answer this particular question but it relates to the title. I had the same issue here is the wrong query
$offer = Offer::with([
'images:name,id,offer_id',
'offer_options:offer_option,value,id,offer_id',
'user:id,name,avatar'])
->select(['id', 'views', 'type', 'status'])
->where('id', $id)->get();
the model look like this
class Offer extends Model {
function user(): BelongsTo {
return $this->belongsTo(User::class);
}
}
The User
class User extends ..... {
function offer(): HasMany {
return $this->hasMany(Offer::class);
}
}
The issue with the query is I was not selecting user_id, i.e in my select function user_id column was not included and that is why I was getting null for user
according to Laravel docs
When using this feature, you should always include the id column and
any relevant foreign key columns in the list of columns you wish to
retrieve.
So the correct query is
$offer = Offer::with([
'images:name,id,offer_id',
'offer_options:offer_option,value,id,offer_id',
'user:id,name,avatar'])
->select(['id', 'views', 'type', 'status','user_id'])
->where('id', $id)->get();