Returning data from any table is simply easy.
Let's say :
I want to retrieve the data from my user table as json format.
I can just simply do this. return Response::json(User::all());
But what if, I want to return the data from my users table and ALL of it's related data as json format.
How do I do that ?
Is there any Laravel mechanism that help me do that ?
Is there any php functions that does this trick ?
If anyone of you have any experience with this, and like to share, please feel free.
Thank you for your time. :)
You can eager load the relationships using the with() method:
return Response::json(User::with('address', 'phones')->get());
However, you have to manually specify all the relations (i.e. you have to specify 'address' and 'phones'). As far as I know, there is no built in way to programmatically determine the relationships.
Edit
You should be able to either chain with() calls, or specify all the relations in one with() call. The following statements should all be equivalent:
// chaining with statements
return Response::json(User::with('address')->with('phones')->get());
// passing all relationships as strings
return Response::json(User::with('address', 'phones')->get());
// passing in array of all relationships
return Response::json(User::with(array('address', 'phones'))->get());
Also, the name of the relationship to pass in is the name of the function that defines the relationship. So, if you had the following class:
class User extends Eloquent
{
public function distributor()
{
return $this->hasOne('Distributor');
}
public function download()
{
return $this->hasOne('Download');
}
public function log()
{
return $this->hasOne('Log');
}
}
Your call would look like:
return Response::json(User::with('distributor', 'download', 'log')->get());
Try it
Users::with("userinfo")->get()->toJson();
toJson() Give JSON Response.
Related
I have problem here with query result from Eloquent, I tried to query from DB and put in variable $contractList in my mount() method and the result as expected. But when I tried to retrieve specific data from $contractList with $contractList->find($id), the result not same as in mount() method.
Here is query from mount():
public function mount(){
$contractList = Kontrak::select(['id', 'mou_id'])->with(['order:id,kontrak_id', 'order.worklist:id', 'order.worklist.khs:id,mou_id,worklist_id,khs', 'amdNilai:id,kontrak_id,tgl_surat'])->withCount('amdNilai')->get()
}
Here the result:
But when I tried to find specific data from $contractList, properties that shown not same as in mount:
public function itemSelected($id)
{
//amd_nilai_count not showing
$kontrak = $this->contractList->find($id);
if ($kontrak->amd_nilai_count == 1) {
$this->nilai_amd = $this->calculateNilai($id);
}
}
Here is the result called from itemSelected():
I have tried use get() but the result still problem, how to get same properties same as in mount().By the way im use laravel & livewire.
As i read your comments you seem to mix up ActiveRecords ORM with an Data Mapper ORM. Laravel uses active records.
Laravel will always fetch models on every data operation.
Kontrak::select('name')->find(1); // select name from kontraks where id = 1;
Kontrak::find(1); // select * from kontraks where id = 1;
This will always execute two SQL calls no matter what and the objects on the heap will not be the same. If you worked with Doctrine or similar, this would be different.
To combat this you would often put your logic in services or similar.
class KontrakService
{
public function find(int $id) {
return Kontrak::select(['id', 'mou_id'])->find($id);
}
}
Whenever you want the same logic, use that service.
resolve(KontrakService::class)->find(1);
However, many relationship operations is hard to do with this and then it is fine to just fetch the model with all the attributes.
I got 2 main tables users and pictures. Each user can have same picture(and vice versa) so it have got "hasMany"(and pivot table in between of them) relationship in Elaquent and it works well. Also, I do have as separate table pictures_details. I wonder, is it possible to get the details of pictures_details when accessing from user to picture model?
public function getImages($id, Images $images)
{
return $users->with('images')->find($id);
}
So when I have call like that, can I also get data of pictures_details table? I have following method in my Image model
public function imageDetails(): BelongsTo
{
return $this->belongsTo(ImageDetails::class, 'image_details_id', 'id');
}
So I thought something like
return $users->with('images')->find($id)->with('imagedetails');
Will work but its not. Can you tell me how to achieve this? Or it is wrong approach?
I want to edit ArSeN's answer. May be it would be more correct if
$users->with(['images.imagedetails'])->find($id);
find($id) returns a single model, not the eloquent query, you cant do joins there.
Switch them around:
$users->with('images')->with('imagedetails')->find($id);
I am working with Laravel 5 and I am having issue getting ->wherePivot() to work on a Many-to-Many relationship. When I dd() the SQL it looks like Eloquent is looking for records in the pivot table with a `pose_state`.`pose_id` is null`.
I am hoping it is a simple error and not a bug. Any ideas are appreciated.
Database Structure
pose
id
name
type
state
id
name
machine_name
pose_state
pose_id
state_id
status
Models
Pose
<?php namespace App;
use DB;
use App\State;
use Illuminate\Database\Eloquent\Model;
class Pose extends Model {
public function states()
{
return $this->belongsToMany('App\State')
->withPivot('status_id')
->withTimestamps();
}
public function scopeWithPendingReviews()
{
return $this->states()
->wherePivot('status_id',10);
}
}
State
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class State extends Model {
public function poses()
{
return $this->belongsToMany('Pose')
->withPivot('status_id')
->withTimestamps();
}
}
PosesController function
public function listPosesForReview(){
$poses = Pose::withPendingReviews()->get();
dd($poses->toArray() );
}
SQL
select
`states`.*, `pose_state`.`pose_id` as `pivot_pose_id`,
`pose_state`.`state_id` as `pivot_state_id`,
`pose_state`.`status_id` as `pivot_status_id`,
`pose_state`.`created_at` as `pivot_created_at`,
`pose_state`.`updated_at` as `pivot_updated_at`
from
`states` inner join `pose_state` on `states`.`id` = `pose_state`.`state_id`
where
`pose_state`.`pose_id` is null and `pose_state`.`status_id` = ?
EDIT
When I updated my code to removing the scope it worked. Thanks #Deefour for putting me on the right path! Maybe scope has something else to that I am missing.
public function pendingReviews()
{
return $this->states()
->wherePivot('status_id','=', 10);
}
YET ANOTHER EDIT
I finally got this to work. The solution above was giving me duplicate entries. No idea why this works, but it does, so I will stick with it.
public function scopeWithStatusCode($query, $tag)
{
$query->with(['states' => function($q) use ($tag)
{
$q->wherePivot('status_id','=', $tag);
}])
->whereHas('states',function($q) use ($tag)
{
$q->where('status_id', $tag);
});
}
I think your implementation of scopeWithPendingReviews() is an abuse of the intended use of scopes.
A scope should be thought of as a reusable set of conditions to append to an existing query, even if that query is simply
SomeModel::newQuery()
The idea is that a pre-existing query would be further refined (read: 'scoped') by the conditions within the scope method, not to generate a new query, and definitely not to generate a new query based on an associated model.
By default, the first and only argument passed to a scope method is the query builder instance itself.
Your scope implementation on your Pose model was really a query against the states table as soon as you did this
$this->states()
This is why your SQL appears as it does. It's also a clear indicator you're misusing scopes. A scope might instead look like this
public function scopeWithPendingReviews($query) {
$query->join('pose_state', 'poses.id', '=', 'pose_state.pose.id')
->where('status_id', 10);
}
Unlike your new pendingReviews() method which is returning a query based on the State model, this scope will refine a query on the Pose model.
Now you can use your scope as you originally intended.
$poses = Pose::withPendingReviews();
which could be translated into the more verbose
$poses = Pose::newQuery()->withPendingReviews();
Notice also the scope above doesn't return a value. It's accepting the existing query builder object and adding onto it.
The other answer to this question is filled with misinformation.
You cannot use wherePivot() as is claims.
Your use of withTimestamps() is not at all related to your problem
You don't have to do any "custom work" to get timestamps working. Adding the withTimestamps() call as you did is all that is needed. Just make sure you have a created_at and updated_at column in your join table.
I think that your implementation of scopes is fine, the problem I see is just a typo. Your schema shows that the field is called status but your where condition is referring to a status_id
Try:
->wherePivot('status', 10);
Also, the withTimestamps() method is causing issues. You don't have timestamps in your schema for the pivot (as I can see) so you shouldn't be putting these in the your relation definitions as it's trying to fetch the timestamps relating to when the relation was created/updated. You can do this if you set up your pivot table schema to have the timestamp fields, but I think you'll have to do some custom work to get the timestamps to save properly.
This worked for me (Laravel 5.3):
$task = App\Models\PricingTask::find(1);
$task->products()->wherePivot('taggable_type', 'product')->get();
You can also have this problem (return no results) if the column you are using in wherePivot hasn't been added to withPivot.
Two of my tables (clients and products) have a ManyToMany relation using Laravel's blongToMany and a pivot table.
Now I want to check if a certain client has a certain product.
I could create a model to check in the pivot table but since Laravel does not require this model for the belongsToMany method I was wondering if there is another way to check if a certain relationship exists without having a model for the pivot table.
I think the official way to do this is to do:
$client = Client::find(1);
$exists = $client->products->contains($product_id);
It's somewhat wasteful in that it'll do the SELECT query, get all results into a Collection and then finally do a foreach over the Collection to find a model with the ID you pass in. However, it doesn't require modelling the pivot table.
If you don't like the wastefulness of that, you could do it yourself in SQL/Query Builder, which also wouldn't require modelling the table (nor would it require getting the Client model if you don't already have it for other purposes:
$exists = DB::table('client_product')
->whereClientId($client_id)
->whereProductId($product_id)
->count() > 0;
The question is quite old but this may help others looking for a solution:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
No "wastefulness" as in #alexrussell's solution and the query is more efficient, too.
Alex's solution is working one, but it will load a Client model and all related Product models from DB into memory and only after that, it will check if the relationship exists.
A better Eloquent way to do that is to use whereHas() method.
1. You don't need to load client model, you can just use his ID.
2. You also don't need to load all products related to that client into memory, like Alex does.
3. One SQL query to DB.
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
Update: I did not take into account the usefulness of checking multiple relations, if that is the case then #deczo has a way better answer to this question. Running only one query to check for all relations is the desired solution.
/**
* Determine if a Client has a specific Product
* #param $clientId
* #param $productId
* #return bool
*/
public function clientHasProduct($clientId, $productId)
{
return ! is_null(
DB::table('client_product')
->where('client_id', $clientId)
->where('product_id', $productId)
->first()
);
}
You could put this in you User/Client model or you could have it in a ClientRepository and use that wherever you need it.
if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
return 'Awesome';
}
But if you already have defined the belongsToMany relationship on a Client Eloquent model, you could do this, inside your Client model, instead:
return ! is_null(
$this->products()
->where('product_id', $productId)
->first()
);
#nielsiano's methods will work, but they will query DB for every user/product pair, which is a waste in my opinion.
If you don't want to load all the related models' data, then this is what I would do for a single user:
// User model
protected $productIds = null;
public function getProductsIdsAttribute()
{
if (is_null($this->productsIds) $this->loadProductsIds();
return $this->productsIds;
}
public function loadProductsIds()
{
$this->productsIds = DB::table($this->products()->getTable())
->where($this->products()->getForeignKey(), $this->getKey())
->lists($this->products()->getOtherKey());
return $this;
}
public function hasProduct($id)
{
return in_array($id, $this->productsIds);
}
Then you can simply do this:
$user = User::first();
$user->hasProduct($someId); // true / false
// or
Auth::user()->hasProduct($someId);
Only 1 query is executed, then you work with the array.
The easiest way would be using contains like #alexrussell suggested.
I think this is a matter of preference, so unless your app is quite big and requires a lot of optimization, you can choose what you find easier to work with.
Hello all) My solution for this problem: i created a own class, extended from Eloquent, and extend all my models from it. In this class i written this simple function:
function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}
For make a check existing relation you must write something like:
if ($user->have('subscribes', 15)) {
// do some things
}
This way generates only a SELECT count(...) query without receiving real data from tables.
To check the existence of a relationship between 2 models, all we need is a single query against the pivot table without any joins.
You can achieve it using the built-in newPivotStatementForId method:
$exists = $client->products()->newPivotStatementForId($product->id)->exists();
use trait:
trait hasPivotTrait
{
public function hasPivot($relation, $model)
{
return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count();
}
}
.
if ($user->hasPivot('tags', $tag)){
// do some things...
}
This has time but maybe I can help someone
if($client->products()->find($product->id)){
exists!!
}
It should be noted that you must have the product and customer model, I hope it helps,
So I have a many to many relationship between Users and Photos via the pivot table user_photo. I use belongsToMany('Photo') in my User model. However the trouble here is that I have a dozen columns in my Photo table most I don't need (especially during a json response). So an example would be:
//Grab user #987's photos:
User::with('photos')->find(987);
//json output:
{
id: 987,
name: "John Appleseed",
photos: {
id: 5435433,
date: ...,
name: 'feelsgoodman.jpg',
....// other columns that I don't need
}
}
Is it possible to modify this method such that Photos model will only return the accepted columns (say specified by an array ['name', 'date'])?
User.php
public function photos()
{
return $this->belongsToMany('Photo');
}
Note: I only want to select specific columns when doing a User->belongsToMany->Photo only. When doing something like Photo::all(), yes I would want all the columns as normal.
EDIT: I've tried Get specific columns using "with()" function in Laravel Eloquent but the columns are still being selected. Also https://github.com/laravel/laravel/issues/2306
You can use belongsToMany with select operation using laravel relationship.
public function photos()
{
return $this->belongsToMany('Photo')->select(array('name', 'date'));
}
Im assuming you have a column named user_id. Then you should be able to do the following:
public function photos()
{
return $this->belongsToMany('Photo')->select(['id', 'user_id', 'date', 'name']);
}
You have to select, the foreign key, else it will have no way of joining it.
Specifying the exact columns you want for the Photos relationship will likely end up biting you in the butt in the future, should your application's needs ever change. A better solution would be to only specify the data you want to return in that particular instance, i.e. the specific JSON response you're delivering.
Option 1: extend/overwrite the toArray() Eloquent function (which is called by toJson()) and change the information returned by it. This will affect every call to these methods, though, so it may end up giving you the same problems as doing select() in the original query.
Option 2: Create a specific method for your JSON response and call it instead of the general toJson(). That method could then do any data building / array modifications necessary to achieve the specific output you need.
Option 3: If you're working with an API or ajax calls in general that need a specific format, consider using a library such as League/Fractal, which is built for just such an occasion. (Phil is also working on a book on building APIs, and it doesn't suck.)