On one of my Laravel 5.4 models I've got multiple accessors. Like this:
public function getRevenueAttribute()
{
return $this->calculate()->revenue($this->price, $this->amount);
}
The problem is that when I pass the model to my vue component:
<product :prp-product="{{json_encode($product)}}"></product>
product.revenue does not exists. Is there a way to achieve this? I don't want to calculate these things in vue again.
Thanks a lot.
You'll need to add an appends Attribute to your model see this for more info.
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['revenue'];
Related
I want to add a key while I'm getting any response of an API using Models in laravel,
Currently, I'm developing an API to get a response with a new one key to add to dynamically.
If I'm not wrong I guess you are trying to create a new field which is not present in table but need to be created dynamically with some logic inside it and you want to pass that field to each and every product of yours.
If so than I guess Laravel Eloquent Accessor will be a best option to use in this case. With the help of Accessor you can create any kind of field and can call it as same as we are calling others fields.
for more reference please take a look at this https://laravel.com/docs/5.7/eloquent-mutators Laravel Documentation.
A sample picked from Laravel Documentation.
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
Now I can use full_name inside my controller or blade files like $user->full_name and than it will give me the concatenated string of first_name and last_name.
Add the attribute to $appends.
class MyModel extends Model {
...
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['extra'];
public function getExtraAttribute(){
return "Extra Value";
}
...
}
first of all in your model add this to add custom field
protected $appends = ['is_favorite'];
public function getIsFavoriteAttribute($value)
{
return $this->name; // whatever you want add here
}
But it problem while you getting other models details while applying relations and select from relation.
I want to achieve this annotation in my controller, but i can not find any documentation or a way to have a wildcard(*) prefix in my annotation inside the controller.
/**
* #Route("/project/*/{Alias}", name="front-story-page" )
*/
public function ShowStoryFront(Story $story)
{
..
}
I tried a whole bunch of different ways but nothing seems to work!
have you simply tried to add another param ?
/**
* #Route("/project/{WildCardParam}/{Alias}", name="front-story-page" )
*/
Assume two Eloquent models in Laravel - user and location.
User:
class User extends Model
{
/**
* Relations that should be automatically loaded.
*
* #var array
*/
public $with = [
'locations'
];
/**
* Returns relation to the locations table.
*/
public function locations() {
return $this->belongsTo('App\Entities\Location');
}
}
Location:
class Location extends Model
{
/**
* Relations that should be automatically loaded.
*
* #var array
*/
public $with = [
'users'
];
/**
* Returns relation to the users table.
*/
public function users() {
return $this->hasMany('App\User');
}
}
As you can see, they both have the public attribute with - because as these models are driving an API, I want the users to always be returned with their location, and locations to always be returned with a list of their users.
However, as I thought might happen, this has led to the request timing out due to (I think) recursion. In other words, the location loads its users, and they load their locations, which then load their users, and so on.
Is there any way in Laravel to force it not to recurse into the same model that is doing the 'initial loading'?
I should mention that user also has another relation, profile, that I want to load in every query. So if, for example, there's a way to simply kill with eager-loading after a certain 'depth', that won't do it.
EDIT: I am aware that I can use User::with('location')->find(); to eager-load the relations, however this solution requires me to use that extra code everywhere I am retrieving users. I am looking for a solution that will let me always eager-load the relationships automatically.
It makes a infinite loop of loading for example you called a User now it is loading Location but when Location loaded then it again called User it has been loaded and the same loading process is going on so it makes a loop which is not even breaking. that why it is give a error
you can do like this
User::with('location')->find(..) // or get or any
You can resolve this problem, just put other model in belongsTo method.
Create new empty JustLocation model extends Location model.
Set protected $with = []; in JustLocation model
Change return $this->belongsTo('App\Entities\Location'); to return $this->belongsTo('App\Entities\JustLocation'); in the User model.
I have 2 eloquent models:
EloquentUser
and
SharedEvents
They are both related by user_id
I'm attempting to set up and appends attribute in the SharedEvents model that will append the full_name of the user with whom the event has been shared.
For the sake of readability, I'm only including the appends components of my class
class SharedEvents extends Model {
protected $appends = ['fullName'];
/**
* #return BelongsTo
*/
public function user() : BelongsTo {
return $this->belongsTo('Path\To\EloquentUser', 'shared_with_id', 'user_id');
}
/**
* #return mixed
*/
public function getFullNameAttribute(){
return $this->user->user_full_name;
}
Unfortunately when I run this I'm getting back both the full name and the entire user model when I only want the full name.
Is there a way to avoid attaching the content of the user model?
It feels like you're trying to make columns from your EloquentUser model first class citizens in your SharedEvent model. You're getting close, but consider...
When working with relationships, this is a good way to be explicit:
Assuming user_full_name is an accessor on your User model:
// EloquentUser.php
// This will automatically add your accessor to every query
// as long as you select the columns the accessor is made
// up of
protected $appends = ['user_full_name'];
/**
* User Full Name Accessor
* #return string
*/
public function getUserFullNameAttribute()
{
return $this->first_name . ' ' . $this->last_name;
}
// SharedEvent.php
/**
* A SharedEvent belongs to an Eloquent User
* #return BelongsTo
*/
public function user() : BelongsTo
{
return $this->belongsTo('Path\To\EloquentUser', 'shared_with_id', 'user_id');
}
// Somewhere in your controller or wherever you want to access the data
$sharedEvents = SharedEvent::with(['user' => function ($query) {
$query->select('user_id', 'first_name', 'last_name');
}])->where(...)->get(['shared_with_id', ...other columns]); // you must select the related column here
This should get you the closest to what you want, but there are a couple of things you should know:
If user_full_name is an accessor, you need to select all of the columns that make up that accessor (as I mention above)
You must select the related keys (user_id in EloquentUser and shared_with_id in SharedEvent)
The $appends is necessary in EloquentUser here because you can't directly add an accessor to your sub query inside the closure.
Try to get comfortable with using a closure as the 2nd argument in your relationships. It's the best way to really be precise as to which columns you're selecting when you're loading relationships — Eloquent makes it really easy to be lazy and just do:
SharedEvent::with('user')->get();
which as you've see will just do a select * on both SharedEvent and your user relationship.
Another thing I've noticed when working with complex queries that use relationships is that you can quickly reach a point where it feels like you're fighting the framework. That's often a sign to consider simplifying ot just using raw SQL. Eloquent is powerful, but is just another tool in your programming tool belt. Remember that you have other tools at your disposal.
I was running into the same problem, and my first idea was to explicitly hide the (entire) associated model when fetching SharedEvent.
In your case, this would look like:
class SharedEvents extends Model {
protected $appends = ['fullName'];
protected $hidden = ['user'];
...
}
I actually decided against this since I am returning a lot of other attached models, so I decided on attaching the entire user, but I tested it and it works.
FYI, I'm not sure if the discrepancy is due to an older version of Laravel, but Eloquent actually expects $appends attributes to be in snake-case (https://laravel.com/docs/5.6/eloquent-serialization#appending-values-to-json):
Note that attribute names are typically referenced in "snake case", even though the accessor is defined using "camel case"
Can you try this?
public function user() : BelongsTo {
return $this->belongsTo('Path\To\EloquentUser', 'shared_with_id', 'user_id')->select('fullName','user_id');
}
After doing some research and experimentation, it doesn't look like there's a way to do this with Eloquent. The solution I ended up going with unfortunately is running an additional query.
/**
* This is inefficient, but I could not find a way to get the full name
* attribute without including all of user.
*
* #return string
*/
public function getFullNameAttribute(){
return EloquentUser::select('user_full_name')
->where('user_id', $this->shared_with_id)
->first()
->user_full_name;
}
Imagine I have a couple of simple objects like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany("App\Post");
}
}
class Post extends Model
{
public function user()
{
return $this->belongsTo("App\User");
}
}
We'll say the \App\Post object has an database column called jsondata which contains JSON-encoded data. When I want to display the user's posts in a view with that column decoded, I need to do this in the controller:
$posts = Auth::user()->posts()->get();
foreach ($posts as $post) {
$post->jsondata = json_decode($post->jsondata);
}
return view("user.show", ["posts"=>$posts]);
Is there a way to avoid that foreach loop in my controller and do the JSON decoding at a lower level?
I'm sure I could do this in App\User::posts() but that doesn't help other places where I need to display the decoded data. I tried defining App\Post::get() to override the parent method, but it doesn't work because hasMany() doesn't seem to return an instance of the model at all.
It can be done in different places/ways, but I would suggest to use an append for this property in your model if you want this data is always decoded everywhere and every time you retrieve a Post model, or simply a mutator.
see https://laravel.com/docs/master/eloquent-mutators
In your model you can define:
protected $appends = [
'name_of_property'
];
// calculated / mutated field
public function getNameOfPropertyAttribute()
{
return jsondecode($this->jsondata);
}
You then can always access this property with:
$post->name_of_property
Note the conversion from CamelCase to snake_case and the conversion from getNameOfPropertyAttribute > name_of_property. By default you need to respect this convention to get it working automagically.
You can substitute the name_of_property and NameOfProperty with what you want accordingly.
Cheers
Alessandro's answer seemed like the best one; it pointed me to the Laravel documentation on accessors and mutators. But, near the bottom of the page is an even easier method: attribute casting.
In typical Laravel fashion, they don't actually list all the types you can cast to, but they do mention being able to cast a JSON-formatted database column to an array. A little poking through the source and it turns out you can do the same with an object. So my answer, added to the App\Post controller, is this:
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = ["jsondata"=>"object"];
This automatically does the decode and makes the raw data available. As a bonus, it automatically does a JSON encode when saving!