I'm trying to convert data retrieved from my Laravel model to a JSON object as outlined in the Backbone docs.
My problem is that when I encode the data all I get are the public properties and none of the (protected) attributes - the ones I actually want. This is how I go about it:
Controller
$movie = Movie::with('awards.award', 'customAwards.awardCustom', 'cast.person', 'imdb.rottenTomatoes')->find($id);
return View::make('movie')->with(array(
'movie' => $movie
));
View
<script type="text/javascript">
DS.Resources.Movie = {{json_encode($movie)}};
</script>
DS.Resources.Movie output
{
"timestamps":false,
"incrementing":true,
"exists":true
}
If I var_dump $movie I can see the protected attributes like title, year, a cast collection, awards collection etc. How do I access these properties and map them to my JSON object?
In Eloquent, you should use ->toJson() as it correctly only gets the model's attributes, rather than general class properties. Similarly, it'll get your relationships if you used a ->with() I think.
If you don't want certain attributes to come out in the JSON (like password fields) you can specify an array in your class called hidden, see the docs.
You should unprotect them in your model.
Also you should realize you can use ->toJson() on your model instead of json_encode'ing it.
Related
Still practicing in Laravel. At this time, I have the a page controller that finds all the sections available to the corresponding page (simple HTML sections) and includes them in the blade like this:
#foreach(#sections as #section)
#include('sections.' . $section->filename)
Then I have a sections table with data for the corresponding section, as well as columns column with a JSON data inside, that looks like this:
{"header":"Section column 1", "text":"bruh?", "image_id":""},
{"header":"Section column 2", "text":"bruh?", "image_id":""},
{"header":"Section column 3", "text":"bruh?", "image_id":"7"}
As you can see, there is image_id that should call for filename from the images table using Eloquent relationship (I assume it's gonna be One to Many).
Yes, I know that storing JSON inside the table is not the best solution (especially if you want to cross-request data from it), but it is the best I came up with.
So, the question(s) is: how can I get the filename of the image using just ID from a JSON object or is there any different solution, except JSON?
P.S. columns table and JSON format is used for defining some properties, since some of my sections have 3-9 text columns (col-md-3), and each of them has a different header, text and image, so I store them inside JSON.
P.P.S. My JSON format is valid, I just removed some formatting in order to keep this question clear.
Laravel has no native support for JSON relationships.
I've created a package for this: https://github.com/staudenmeir/eloquent-json-relations
class Section extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'columns' => 'json',
];
public function image()
{
return $this->belongsTo(Image::class, 'columns->image_id');
}
}
class Image extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function sections()
{
return $this->hasMany(Section::class, 'columns->image_id');
}
}
{{ $section->image->filename }}
I have a one-to-one relationship between User and UserSettings models,
But (after $user = auth()->user()) when I try $user->settings()->something it throws an Undefined property error.
It's gone when I use $user->settings()->first()->something...
My question is, is this how it's supposed to work? or am I doing something wrong?
You cannot directly run $user->settings()->something.
Because when you call $user->settings(), it just return Illuminate\Database\Eloquent\Relations\HasOne object.
So it is not the model's object, you need to take the model's object and call its attribute like this.
$user->settings()->first()->something;
Dynamic Properties
Since you have one-to-one relationship between User and UserSettings.
If you have a one-to-one relationship in your User model:
public function settings()
{
return $this->hasOne('App\Models\UserSettings', 'user_id', 'id');
}
According to Laravel doc
Once the relationship is defined, we may retrieve the related record using Eloquent's dynamic properties. Dynamic properties allow you to access relationship methods as if they were properties defined on the model:
Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the get (for one-to-many relationships) or first (for one-to-one relationships) method. It will then be accessible via a dynamic property by the same name as the relation.
So you can use eloquent's dynamic properties like this:
$user->settings->something; // settings is the dynamic property of $user.
This code will give you a result of collection.
$user->settings;
So calling 'something' is not available or it will return you of null, unless you get the specific index of it.
$user->settings()->something
while this one works because you used first() to get the first data of collection and accessed the properties of it .
$user->settings()->first()->something
The first method returns the first element in the collection that passes a given truth test
see docs here laravel docs
If you want to get the user settings itself simply do this:
$user->settings
Then you can get the fields of the settings doing this:
$user->settings->something
When you do this $user->settings() you can chain query after that. E.g.
$user->settings()->where('something', 'hello')->first()
That's why the output of $user->settings and $user->settings()->first() are the same.
Auth only gives you user info;
Try the following code:
$user = User::find(auth()->user()->id);//and then
$user->settings->something;
I have a classic one-to-many relationships, and I am trying to save the model of the belongsTo side.
The 2 models have these relationships:
// Model myModel
function domicile()
{
return $this->belongsTo('App\Address', 'domicile_id');
}
// Model Address
function myModels()
{
return $this->hasMany('App\MyModel', 'domicile_id');
}
This is what I am tryng to do to save it:
$myModel->domicile()->save($my_array);
With this code I get the error:
Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsTo::save()
if I use this code (without the brackets):
$myModel->domicile->save($my_array);
I do not get any error but the model is not saved.
I know there is the method associate, but I need to update an existent record, not to save a new one.
Because $myModel->domicile()->save($my_array); is totally different to $myModel->domicile->save($my_array); :
$myModel->domicile() will produce a BelongsTo object, doesn't support the save because save is a method of HasMany instances, instead for BelongsTo instances you should use associate(YourModel)
$myModel->domicile will produce a Model object of the associated element, which support the save(array) method, but that array is a options array, as api says https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Model.html#method_save
So in other words, if you have a one (address) to many (domicile) relation, if you want to associate to the address one or many domiciles, you have to use save or saveMany (https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Relations/HasMany.html#method_save), instead if you want to associate to a domicile a address, you should use associate (https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Relations/BelongsTo.html#method_associate)... keep in mind that if you want to do this, you should call the properties with the brackets, in order to have back a HasMany object or a BelongsTo object, and not a Model or a Collection (which you will get if you call the properties without the brackets)
Instead of using the save function, in order to save a belongsTo relationships you have to use the fill function.
In this way:
$myModel->domicile->fill($my_array);
$myModel->domicile->save();
You must use associate() + save() in order to store a BelongsTo relationship:
$myModel->domicile()->associate($domicile);
$myModel->save();
See Laravel Docs
I am working on a project which requires me to get all the list of all information from a table --Just like in a blog, i used the all() method to do this but when i try to get the method i declared in my Model i get an error, saying
the collection instance does not exists
But when i use The
Model::find($id)->relationship()->name;
it works fine. Is there any way to load all relationship with the all() function in laravel.
Thanks for your help..
When you perform Model::find($id)->relationship(); you are actually accesing to the Dynamic relationships Properties
You need to convert it into a collection using Model::find($id)->relationship()->get();
Then you can perform any collection method to get the result you want. After doing this you can access to its attributes like this:
$model_varible = Model::find($id)->relationship()->get();
$model_variable = $model_variable->find($id)->name;
Let me know if this works for you.
You should use relationship without brackets to access the model:
Model::find($id)->relationship->name;
And use "with()" to populate the relationships:
Model::where('published', 1)->with('relationship')
A table column in my database is saved as json type. I want to display a keyed value in the saved json object on my blade templates but I'm unsure how.
Let's say I have $table->json('meta') in my table schema for Newsletter model, with meta column having e.g. {"foo": "bar"} as value.
How would I retrieve something like $newsletter->meta->foo? Since $newsletter->meta returns string instead of json object by default on Laravel 5.5 requiring a trivial json_decode to convert it.
A cleaner solution besides json_decode on every call, would be to use an accessor on that column e.g. getMetaAttribute but that's still messy. I want automatic json column to PHP object detection, how can I make this happen?
You can declare a protected $casts array inside your model in which you can instruct Eloquent to automatically convert types of model attributes. In your case that would look like this:
/*
* #property string $meta - json is actually just a string
*/
class Newsletter extends Model {
protected $casts = [
'meta' => 'array',
];
}
// Now you can use `$meta` as array:
$newsletter = Newsletter::find(1);
$foo = array_get($newsletter->meta, 'foo');
But this still does not convert it into an object. Although object is mentioned in the docs as being a valid cast type, I can not tell you exactly what it does.
As a note, these $casts are not bi-directional. If you want to set meta on a Newsletter instance, you'd have to create your own facilities to convert any object or array into a json string. Eloquent allows you to define mutators on your model to get the job done.