I have a table, table name is bookings and here have a column e_provider. this column i direct fetch data by id and save all data in e_provider field
how can i access this e_provider data like $data->e_provider->name
here is code
[
{
"id": 2,
"e_provider": "{"id":11,"name":"Architect O'Reilly, Ratke and Miller","phone_number":"661.425.3559","mobile_number":"307.607.7472"}",
}
]
in laravel 8 and less try this
Defining A Accessors & Mutators
public function getEProviderAttribute(){
return json_decode($this->getAttributeValue('e_provider'));
}
public function setEProviderAttribute($value){
return json_encode($value);
}
For update value
$e_provider = $object->e_provider;
$e_provider->name = "new name";
$object->e_provider = $e_provider;
$object->save();
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
You can use $casts in your model
class BookingModel extends Model
{
protected $casts = [
'e_provider' => AsCollection::class,
];
}
Now you will be able to get the data by the following:
$bookingModel->e_provider->id
https://laravel.com/docs/9.x/eloquent-mutators#array-object-and-collection-casting
Another way if you don't want to use casts, you can define an accessor:
protected function eProvider(): Attribute
{
return Attribute::make(
get: fn ($value) => json_decode($value),
set: fn ($value) => json_encode($value),
);
}
https://laravel.com/docs/9.x/eloquent-mutators#defining-an-accessor
Related
Fiddling with Laravel and coming from Symfony, I'm trying to replicate some code.
I'm trying to PUT a Suggestion model (overwritting anything, even relationships) and wanted to know the proper way to overwrite the model.
Since tags attribute in fillable doesn't exist, I certainly get an error (Undefined column: 7 ERROR: column "tags" of relation "suggestions" does not exist).
Suggestions and tags both have their own tables and a pivot table that contains two foreign keys to both tables id.
Request & Response :
{
"id":2,
"content":"Magni.",
"tags":[{"id":13,"name":"MediumAquaMarine"}]
}
{
"id":2,
"content":"Magni.",
"tags":[{"id":10,"name":"Navy"},{"id":13,"name":"MediumAquaMarine"}]
}
public function update(Request $request, Suggestion $suggestion)
{
$validator = Validator::make($request->all(), [
'content' => 'required',
'tags.id' => 'numeric',
]);
if ($validator->fails()) {
return response()->json($validator->messages(), Response::HTTP_BAD_REQUEST);
}
$suggestion->fill($request->only($suggestion->getFillable()))->save();
return new SuggestionResource($suggestion);
}
class Suggestion extends Model
{
use HasFactory;
protected $fillable = ['content', 'tags'];
protected $with = ['tags'];
public function tags()
{
return $this->belongsToMany(Tag::class, 'suggestions_tags')->withTimestamps();
}
}
class Tag extends Model
{
use HasFactory;
protected $hidden = ['pivot'];
public function suggestions()
{
return $this->belongsToMany(Suggestion::class, 'suggestions_tags')->withTimestamps();
}
}
You could just pass an array of IDs for tags instead of the whole object.
Do:
"tags":[10, 13]
Instead of:
"tags":[{"id":10,"name":"Navy"},{"id":13,"name":"MediumAquaMarine"}]
Change the validation rules accordingly and then you can remove tags from $fillable and do something like:
$suggestion->update($request->validated());
$suggestion->tags()->sync($request->tags);
I'm trying to get a custom attribute (https://laravel.com/docs/5.5/eloquent-mutators#defining-an-accessor)
from a query.
Right now I have:
User.php
public function getViewUrlAttribute()
{
return route('admin.users.view', ['id' => $this->id]);
}
public function role()
{
return $this->belongsTo('App\Role')->withDefault([
'name' => 'User'
]);
}
UserController.php
public function dataTable(Request $request)
{
$length = $request->has('length') ? $request->input('length') : 10;
$orderDirection = $request->input('orderDirection');
$searchValue = $request->input('search');
$users = User::select('id', 'name', 'email', 'created_at')->with('role:name')->limit($length);
if ($request->has('orderBy')) {
if ($request->has('orderDirection')) {
$users = $users->orderBy($request->input('orderBy'), $request->input('orderDirection') > 0 ? 'asc' : 'desc');
} else {
$users = $users->orderBy($request->input('orderBy'), 'desc');
}
}
return $users->get();
}
Returns
[
{
"id": 1,
"name": "User",
"email": "user#test.com",
"created_at": "2018-04-24 14:14:12",
"role": {
"name": "User"
}
}
]
So the thing is: there's any way to also get the view_url attribute? (I tried inside the with() but it fails)
Also can I return only the role name and not the whole object as you can see in the "Return" code? (I would like something like: "role": "User").
(Of course I'm trying to avoid running raw sql)
Thanks!
You're almost done...
1- To add a custom attribute you need to append it on the Model with $appends attribute:
protected $appends = ['view_url'];
And define your attribute method:
public function getViewUrlAttribute()
{
return route('admin.users.view', ['id' => $this->id]);
}
2- To add attribute to a model from another related model, I think you should try :
// to add them as attribute automatically
protected $appends = ['view_url', 'role_name'];
// to hide attributes or relations from json/array
protected $hidden = ['role']; // hide the role relation
public function getRoleNameAttribute()
{
// if relation is not loaded yet, load it first in case you don't use eager loading
if ( ! array_key_exists('role', $this->relations))
$this->load('role');
$role = $this->getRelation('role');
// then return the name directly
return $role->name;
}
Then you might not event need ->with('role') eager loading.
I'm performing validation of a form, where a user may select a range of values (based on a set of entries in a model)
E.g. I have the Model CfgLocale(id, name)
I would like to have something like:
CfgLocale->listofAvailableIds() : return a array
What I did is:
Inside Model this method:
class CfgLocale extends Model
{
protected $table = 'cfg_locales';
public static function availableid()
{
$id_list = [];
$res = self::select('id')->get();
foreach($res as $i){
$id_list[] = $i->id;
}
return $id_list;
}
}
On Controller for validation I would do then:
$this->validate($request, [
'id' => 'required|integer|min:1',
...
'locale' => 'required|in:'.implode(',', CfgLocale::availableid()),
]);
Any better Idea, or Laravel standard to have this done?
Thanks
You can use exists rule of laravel.You can define a validation rule as below. Might be this can help.
'locale' => 'exists:cfg_locales,id'
Use this code instead,
class CfgLocale extends Model
{
protected $table = 'cfg_locales';
public static function availableid()
{
return $this->pluck('id')->toArray();
}
}
pluck method selects the id column from your table and toArray method converts your model object collection into array.
Know more about Laravel Collections here.
This will return an array of IDs:
public static function availableid()
{
return $this->pluck('id')->toArray();
}
https://laravel.com/docs/5.3/collections#method-pluck
https://laravel.com/docs/5.3/collections#method-toarray
I have two Model classes with a oneToMany relation like this
App\Car
class Car extends Model
{
public $timestamps = true;
protected $fillable = [
'name',
'price'
];
public function parts ()
{
return $this->hasMany('App\Part');
}
}
App\Part
class Part extends Model
{
public $timestamps = false;
protected $fillable = [
'name',
'price'
];
public function car ()
{
return $this->belongsTo('App\Car');
}
}
The client makes a POST request with a JSON representing a Car with a nested Array of Parts
{
"name": "Fiat Punto",
"price": 15000,
"parts": [
{
"name": "wheel",
"price": 300
},
{
"name": "engine",
"price": 5000
}
]
}
Is there a way to save the Car model and create the relations in just one hit?
I tried to do this in my controller but it doesn't work:
...
public function store (Request $request) {
$input = $request->all();
Car::create($input);
}
...
PS: I already know how to do the task with a foreach or array_reduce, just wondering if laravel could do that for me
-- Edit --
Here is how i implemented the controller right now:
...
public function store (Request $request) {
$input = $request->all();
$car = Car::create($input);
$parts = array_reduce(
$input['parts'],
function ($carry, $item) {
array_push($carry, new Part($item));
return $carry;
},
[]
);
$car->parts()->saveMany($parts);
}
...
Any improvement is welcome
I donĀ“t think there is a way of getting around the fact that you need to create each of the App\Part model instances that are defined in the request object. So, at some point you have to iterate over those items in the request object, meaning you have (at least) two options. Those are both described here.
On a side note I think in this case it is better to use a foreach loop for the first option:
foreach ($request->input('parts') as $item) {
$car->parts()->save(new Part($item));
}
If you'd go for the second option of storing them in an array first, then I think array_map is the more appropriate method to use (because there is no actual "reducing" going on):
$parts = array_map(function($part) {
return new App\Part($part);
}, $request->input('parts'));
$car->parts()-saveMany($parts);
Edit: As per suggestion from #TomasKim, with only 2 queries - one for the Car-model and another for the Part-models:
$parts = array_map(function($part) {
return [...$part, 'car_id' => $car->id];
}, $input['parts'])
DB::table('parts')->insert($parts);
While there isn't a way to technically do this entirely automatically, you can make the eloquent api for your model act this way by just tweaking the fillable property and adding a method:
class Car extends Model
{
public $timestamps = true;
protected $fillable = [
'name',
'price',
'parts',
];
public function parts ()
{
return $this->hasMany('App\Part');
}
public function setPartsAttribute($parts)
{
foreach ($parts as $part) {
Part::updateOrCreate(
['id' => array_get($parts, 'id')],
array_merge($part, ['car_id' => $this->id])
);
}
// ... delete parts not in the list if you want
}
}
Notice we just added parts to the fillable property, and then used Eloquent's attribute setting hook set[Field]Attribute() to perform the assignment. This makes it possible to have the Car api look about right:
Car::find(1)->update($carWithParts);
Note: This won't work when creating the Car model since the there is no id. If you want to automate that too, you can queue these updates and then use the model's event hooks to perform this action.
I've got 2 models with a many-to-many relationship. I want to be able to set a specific attribute with an array of ids and make the relationship in the mutator like this:
<?php
class Profile extends Eloquent {
protected $fillable = [ 'name', 'photo', 'tags' ];
protected $appends = [ 'tags' ];
public function getTagsAttribute()
{
$tag_ids = [];
$tags = $this->tags()->get([ 'tag_id' ]);
foreach ($tags as $tag) {
$tag_ids[] = $tag->tag_id;
}
return $tag_ids;
}
public function setTagsAttribute($tag_ids)
{
foreach ($tag_ids as $tag_id) {
$this->tags()->attach($tag_id);
}
}
public function tags()
{
return $this->belongsToMany('Tag');
}
}
<?php
class Tag extends Eloquent {
protected $fillable = [ 'title' ];
protected $appends = [ 'profiles' ];
public function getProfilesAttribute()
{
$profile_ids = [];
$profiles = $this->profiles()->get([ 'profile_id' ]);
foreach ($profiles as $profile) {
$profile_ids[] = $profile->profile_id;
}
return $profile_ids;
}
public function profiles()
{
return $this->belongsToMany('Profile');
}
}
However the setTagsAttribute function isn't working as expected. I'm getting the following error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'profile_id' cannot be null (SQL: insert intoprofile_tag(profile_id,tag_id) values (?, ?)) (Bindings: array ( 0 => NULL, 1 => 1, ))
You can't attach many-to-many relations until you've saved the model. Call save() on the model before setting $model->tags and you should be OK. The reason for this is that the model needs to have an ID that Laravel can put in the pivot table, which needs the ID of both models.
It looks like you're calling the function incorrectly or from an uninitialized model. The error says that profile_id is NULL. So if you're calling the function as $profile->setTagsAttribute() you need to make sure that $profile is initialized in the database with an ID.
$profile = new Profile;
//will fail because $profile->id is NULL
//INSERT: profile->save() or Profile::Create();
$profile->setTagsAttribute(array(1,2,3));
Additionally, you can pass an array to the attach function to attach multiple models at once, like so:
$this->tags()->attach($tag_ids);
You can also pass it the model instead of the ID (but pretty sure array of models won't work)
Try using the sync method:
class Profile extends Eloquent {
protected $fillable = [ 'name', 'photo', 'tags' ];
protected $appends = [ 'tags' ];
public function getTagsAttribute()
{
return $this->tags()->lists('tag_id');
}
public function setTagsAttribute($tag_ids)
{
$this->tags()->sync($tagIds, false);
// false tells sync not to remove tags whose id's you don't pass.
// remove it all together if that is desired.
}
public function tags()
{
return $this->belongsToMany('Tag');
}
}
Don't access the tags through the tags() function, rather use the tags property. Use the function name if you want to pop additional parameters onto the relationship query and the property if you just want to grab the tags. tags() works in your getter because you're using get() on the end.
public function setTagsAttribute($tagIds)
{
foreach ($tagIds as $tagId)
{
$this->tags->attach($tagId);
}
}