I have small laravel function called from ajax. Then I have to return result to ajax result. For example
Public function Edit(Request $request){
$user = User::find($request->id);
//.........
return response()->json($user);
}
But I have problem on 'salary' property because of It getting from getSalaryAttribute method
public function getPositionnameAttribute(){
...........
}
So, at the ajax result return 'undefined', that reasonable because 'salary' property does not exist. I do not need to made specific collection to handle only salary parameter.
Any advise or guidance would be greatly appreciated, Thanks.
You have two options:
add protected $appends = ['salary'] to your model
or
append it on run time: $user->append('salary')->toArray();
Check out the docs: https://laravel.com/docs/5.8/eloquent-serialization#appending-values-to-json
Appending At Run Time add this attribute to $attributes array,
The append method. Or, you may use the setAppends method to override the entire array of appended properties for a given model instance.
return $user->append('salary')->toArray();
return $user->setAppends(['salary'])->toArray();
You need to add this attribute to $appends array.
Model
protected $appends = ['position_name'];
NOTE: The $hidden and $appends feature (written above) is for modeling of JSON data and will not effect blade access to properties and/or model renderings.
https://laravel.com/docs/8.x/eloquent-serialization#appending-values-to-json
Related
I have created resource controllers, one per model in my laravel 5.8 project. I want the show function to return the DB element i want based on the id inserted on the URL, as it is supposed to do. For now i do tests directly on my controller, i'm not using the "thisCircuit" function of my model. Calling the index, returns a json with all circuits. Calling the show returns nothing. How can i fix it?
Show function
public function show(circuits $circuits)
{
$circuits = circuits::findOrFail($circuits);
dd($circuits);
}
Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class circuits extends Model
{
protected $fillable = [
'circuitId', 'circuitRef', 'name',
'location', 'country', 'lat',
'lng', 'alt', 'url',
];
protected $primaryKey = 'circuitId';
public function races()
{
return $this->hasMany('App\races', 'circuitId');
}
public function allCircuits(){
$data = Circuits::all();
return response()->json($data);
}
public function thisCircuit($id){
$id = circuits::findOrFail($id);
}
}
Web.php File
Route::get('/test', 'CircuitsController#index');
Route::get('/test/{circuit}', 'CircuitsController#show');
URL on browser
http://localhost:8000/test/1
Result on browser
Illuminate\Database\Eloquent\Collection {#947 ▼
#items: []
}
Ok, let's clean up this solution. A lot of smaller stylistic problems, that will hurt you going forward if not adjusted.
Firstly
Class names are starting with capitols letter in most standard naming conventions and in singular form.
circuits.php
Should be.
Circuit.php
Secondly
You are already using model binding. If you are doing this approach you can actually just return the circuit directly. As a bonus Laravel does not have to return response if the returned data is a model, unless you want to change the response code from 200 to something else.
public function show(Circuit $circuit) {
return $circuit;
}
Also you are misunderstanding firstOrFail(). This code you have written can never return multiple Circuits, this will return a singular model.
$circuits = circuits::findOrFail($circuits);
so i just wondered, if something like this is possible, since my code does not work.
protected $appends = ['position_name'];
public function getPositionNameAttribute()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id')->name;
}
Can I append the name of Eloquen relationship model?
edit: so far, i am using this:
foreach ($employees as $e) {
$e->position_name = $e->position->name;
}
So, I needed to use the relation defined before.
protected $appends = ['position_name'];
public function position()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id');
}
public function getPositionNameAttribute()
{
return $this->position->name;
}
Based on your comments i'd suggest to use the laravel default solution for your problems API resrouces
eg
class EmployeeResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'position_name' => $this->position->name,
];
}
}
note: using the with as other people suggested to preload information can increase performance by reducing the amount of queries, if you are returning a collection of employees.
Creating an accessor that looks up a value in another model and appending this field by using $appends is bad practice and will lead to n+1 queries whenever you fetch your Employee model. You should avoid doing this and just use $employee->position->name.
You should also make sure to use Employee::with('position') when you need to show the position name, so that the position model is fetched in a single query.
If the position name is something that you need in all your Employee queries, then you can set the Employee to always eager load the position by defining the following inside your Employee model:
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['position'];
I think you can just create a model with position names and reference it to the position id in the other mode by using eloquent relationships.
When working with other frameworks, or pure-PHP, I protect my model properties. I then create public getters and setters where required, and proxy to them using __get() and __set(). This helps me sleep at night.
Recently I started using Laravel and I am surprised at how 'unprotected' the Eloquent models are. I understand that I can use the $guarded and $fillable properties to control mass assignment, but that still leaves a lot of room for accidental access.
For example, my model has a status property. It has a default value set on model creation, and should only be modified when $model->activate() or $model->deactivate() is called. But by default, Laravel allows developers to modify it directly. As far as I can see, the only way to prevent this is to create a setter, and throw an exception if it is called.
Am I missing something? Perhaps I just need to relax? What's the best way to build Eloquent models that are secure by default?
You can override __get and __set method. You need to define an array protectedProperties and a boolean variable protectedChecks so you can control the model fields.
protected $protectedChecks = true;
protected $protectedProperties = [ 'status' ];
protected $fillable = ['status'];
public function __get($key)
{
return (in_array($key, $this->fillable) && !in_array($key, $this->protectedProperties)) ? $this->attributes[$key] : null;
}
public function __set($key, $value)
{
if(!$this->protectedChecks || !in_array($key, $this->protectedProperties))
return parent::__set($key, $value);
trigger_error('Protected Field');
}
public function activate()
{
$this->protectedChecks = false;
$this->status = 1;
$this->save(); // this is optional if you want to save the model immediately
$this->protectedChecks = true;
}
If you want to use every model you should write something like above in BaseModel.
You may try:
<?php
class User extends Eloquent {
protected $hidden = array('password', 'token');
}
For what i see you probabely coming from symfony or other system that uses Mapping as a base to deal with database layer. Forget what you have done there as Eloquent uses Active Records an is different.
Best way is this: Eloquent: Accessors & Mutators
Alo check in laracast there is explanation how to do it in old php fashion way using absolute properties.
I have created one and I thought it works:
<?php
namespace App\Traits;
use Carbon\Carbon;
trait FormatDates
{
public function setAttribute($key, $value)
{
parent::setAttribute($key, $value);
if (strtotime($value))
$this->attributes[$key] = Carbon::parse($value);
}
}
But there is a problem when calling related models. For example if you have an Article and Tag model and you want to get all tags like this:
$article->tags
it returns null because of that getter mutator.
How to fix this?
update 17.11.2017
I have found a solution to my problem. The best way to present the date in locale is to use this function:
\Carbon\Carbon::setToStringFormat("d.m.Y H:i");
simply create a service provider or a middleware and it will show all $dates in format you want. There is no need to make a getter.
Based from this: https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Concerns/HasAttributes.html#method_getAttribute
The description says:
Get a plain attribute (not a relationship).
Luckily there are another two methods below it called getRelationValue and getRelationshipFromMethod, and it reads:
Get a relationship.
Get a relationship value from a method.
respectively.
And in your example, it looks like you're calling a relation.
I think you should consider it when doing your universal getter/mutator.
UPDATE:
If you inspect the code, the getAttribute also calls the getRelationValue method. But it is the last resort of the function; if the key is neither an attribute or has a mutator or is a method of the class.
Here is the stub: https://github.com/laravel/framework/blob/5.5/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L302
/**
* Get an attribute from the model.
*
* #param string $key
* #return mixed
*/
public function getAttribute($key)
{
if (! $key) {
return;
}
// If the attribute exists in the attribute array or has a "get" mutator we will
// get the attribute's value. Otherwise, we will proceed as if the developers
// are asking for a relationship's value. This covers both types of values.
if (array_key_exists($key, $this->attributes) ||
$this->hasGetMutator($key)) {
return $this->getAttributeValue($key);
}
// Here we will determine if the model base class itself contains this given key
// since we don't want to treat any of those methods as relationships because
// they are all intended as helper methods and none of these are relations.
if (method_exists(self::class, $key)) {
return;
}
return $this->getRelationValue($key);
}
ANOTHER UPDATE
Since you've changed your question:
You can just put the attribute name to $casts or $dates array (in your Model) so Laravel will automatically transform it into a Carbon instance when accessing it, like this:
class Article extends Model {
...
protected $dates = ['some_date_attribute`];
or with $casts
...
protected $casts = ['some_date_attributes' => 'date'];
You really can avoid this, it's already there!
on the model Class you can do:
protected $dates = ['nameOfTheDateOrTimestampTypeField','nameOfAnotherOne'];
I'm using Laravel 5 and I created a resource controller, setup routing and setup my database model.
My question is: when I do a POST, my store method is called on the controller but how can I take the request data and insert a new entry in the db without having to explicitly set every field?
Here's my code:
public function store(Request $request)
{
$data = $request->all(); // this grabs all my request data - great!
$user = new User;
$user-> ??? // insert ALL posted data
$user->save();
}
I understand that I can do...
$user->name = $request->name;
...for every field. But can someone tell me how to insert everything?
I realize I can probably do a foreach but I'm wondering if there's something better.
There is method fill in every model https://laravel.com/api/5.3/Illuminate/Database/Eloquent/Model.html#method_fill:
$user->fill($request->all());
Eventually you can use create:
\User::create($request->all());
This above is called mass asignment and there is section about this in Eloquents documentation https://laravel.com/docs/5.1/eloquent
You need to define which keys might be set:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
// The attributes that are mass assignable
protected $fillable = ['name', 'login', 'birthdate'];
}