How to use Model in function parameter to reduce code in laravel? - laravel

This is how i am trying to update record in my laravel function which doesn't work
public function completePacking(SaleOrder $saleOrder)
{
$saleOrder->update(['status' => 'Draft']);
}
it is working
public function completePacking($id)
{
$saleOrder = SaleOrder::findOrFail($id);
$saleOrder->status = 'Dispatched';
$saleOrder->save();
}
i want to use first method because it is less code but that is not working

Add 'status' to your $fillable attribute in your SaleOrder model.
Or remove 'status' from $guarded attribute in SaleOrder model.
After doing any of the following, you would be able to use your desired version to update status.
Read more on https://laravel.com/docs/5.7/eloquent#mass-assignment

$saleOrder = SaleOrder::where('id', $id)->update(['status' => 'Draft']);

Related

Right way to save timestamps to database in laravel?

As part of a standard laravel application with a vuejs and axios front-end, when I try to save an ISO8601 value to the action_at field, I get an exception.
class Thing extends Model {
protected $table = 'things';
// timestamp columns in postgres
protected $dates = ['action_at', 'created_at', 'updated_at'];
protected $fillable = ['action_at'];
}
class ThingController extends Controller {
public function store(Request $request) {
$data = $request->validate([
'action_at' => 'nullable',
]);
// throws \Carbon\Exceptions\InvalidFormatException(code: 0): Unexpected data found.
$thing = Thing::create($data);
}
}
My primary requirement is that the database saves exactly what time the client thinks it saved. If another process decides to act on the "action_at" column, it should not be a few hours off because of timezones.
I can change the laravel code or I can pick a different time format to send to Laravel. What's the correct laravel way to solve this?
The default created_at and updated_at should work fine.
You should always set your timezone in your config/app.php to UTC
Add a timezone column or whichever you prefer in your users table
Do the time-offsets in your frontend or api response
Here's a sample code to do the time offset in backend
$foo = new Foo;
$foo->created_at->setTimezone('America/Los_Angeles');
or frontend using momentjs
moment(1650037709).utcOffset(60).format('YYYY-MM-DD HH:mm')
or using moment-timezone
moment(1650037709).tz('America/Los_Angeles').format('YYYY-MM-DD HH:mm')
class Thing extends Model {
protected $table = 'things';
// timestamp columns in postgres
protected $dates = ['action_at', 'created_at', 'updated_at'];
protected $fillable = ['action_at'];
}
class ThingController extends Controller {
public function store(Request $request) {
$data = $request->validate([
'action_at' => 'nullable',
]);
// convert ISO8601 value, if not null
if ($data['action_at'] ?? null && is_string($data['action_at'])) {
// note that if the user passes something not in IS08601
// it is possible that Carbon will accept it
// but it might not be what the user expected.
$action_at = \Carbon\Carbon::parse($data['action_at'])
// default value from config files: 'UTC'
->setTimezone(config('app.timezone'));
// Postgres timestamp column format
$data['action_at'] = $action_at->format('Y-m-d H:i:s');
}
$thing = Thing::create($data);
}
}
Other tips: don't use the postgres timestamptz column if you want to use it with protected $dates, laravel doesn't know how to give it to carbon the way postgres returns the extra timezone data.
See the carbon docs for other things you can do with the $action_at instance of \Carbon\Carbon, such as making sure the date is not too far in the future or too far in the past. https://carbon.nesbot.com/docs/

Laravel - How to change response format for specific fields

I've "Product" model.
And need to change some value formats for only responses.
For example;
I've "price" on database as decimal (11,2).
I want this as "1.000.000,00" format on response.
Or created_at field to "Carbon::parse($this->created_at)->toDayDatetimeString()"
Or I want to add 3 specific columns with my user attribute, on response. (is_allowed etc.)
How can this be possible on model?
How can I response like that?
You can use Mutator and Accessor to set format :
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
public function setDateAttribute($date) {
$this->attributes['date'] = Carbon::createFromFormat('Y-m-d', $date);
}
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
As a best practice in Laravel you can use Eloquent Resources: Eloquent Resources
It's basically a "transformer" between models data and API/Responses Output.
The only one thing to notice is that in the Resource files yout must specify all fields and relations (if needed) of the Model manually.
In the toArray() function you can modify the type of all data of your model as you prefer.
If not, you can access the new field by $model->my_custom_field (Laravel can resolve the name of the getter function automatically).
public function toArray($request)
{
$editedFieldValue = doSomething();
return [
'my_field' => $editedFieldValue,
'other_field' => '',
];
}
If you want to do that in Model, you can create customs fields:
class MuModel extends Model
{
protected $appends = ['my_custom_field'];
public function getMyCustomFiledAttribute(){
$newData = doSomething($this->existent_field);
return $newData;
}
}
The $appends variable add the new fields to all responses generated from the Model, as a normal database field.
P.S.: You can create a getAttribute() function for existent database attribute and return the value as you want!
For example: getCreatedAtAttribute()

Laravel insert into database request()->all() and addition

In laravel if i want to insert all the form input and i want to add text in one of the column why cant i use this code?
Example
$B2 = new B2;
$B2::create([
request()->all(),
$B2->column9 = "aaaa",
]);
The inserted database only insert column9, the other column is Null.
Because create() accepts an array as the only parameter:
public static function create(array $attributes = [])
You can do this:
$data = request()->all();
$data['column9'] = 'aaaa';
B2::create($data);
When ever you use request all you must first make sure that you have either fillable fields in your model or guarded = to an empty array so for example:
class B2 extends Model
{
protected $table = 'db_table';
protected $fillable = [
'email',
'name',
];
}
or you can use
protected $guarded = [];
// PLEASE BE CAREFUL WHEN USING GUARDED AS A POSE TO FILLABLE AS IT OPENS YOU TO SECURITY ISSUES AND SHOULD ONLY REALLY BE USED IN TEST ENVIRONMENTS UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!
As for your create method you should make sure its an associative array like this:
$B2::create([
$B2->column9 => "aaaa",
]);
Or you could do something like:
$data = $request->except('_token');
$B2::create($data);
You'll have to merge the array.
$B2::create(array_merge(request()->all(), ['column9' => 'text']));
When you are adding to a database in that was it is called mass assignment. Laravel Automatically protects against this so you need to add the firld names to a fillable attribute in your model
protected $fillable = ['field1', 'column9'] //etc
https://laravel.com/docs/5.4/eloquent#mass-assignment
You also need to make sure you pass an array to the create method
$my_array = $request->all()
$my_array['column9'] = 'aaaa';
$B2::create(
$my_array
);

How to make shortest code?

I use the following code to insert multi array to database:
foreach($request->category as $k => $v){
$category[] = array(
"category_id" => $v,
"announcement_id" => $announcement->id
);
}
AnnouncementCategory::insert($category);
So, input data is POST array $request->category.
I need to refactoring this code
I tried this code:
$announcement->categories()->attach($request->category);
In model Announcement I have:
public function categories()
{
return $this->hasMany("App\AnnouncementCategory", "announcement_id", "id");
}
If you define in your Announcement model relationship like this:
public function categories()
{
return $this->belongsToMany(AnnouncementCategory::class);
}
you can do it like this:
$announcement->categories()->attach($request->category);
EDIT
I see you updated your question and added categories relationship. But looking at your code, AnnounceCategory is rather pivot table, so you should use belongsToMany as I showed instead of hasMany
You can do it in one line if the request matches the columns:
AnnouncementCategory::insert($request->all());
Then in your AnnouncementCategory model, make sure you declare the protected $fillable array where you specify which field could be populated.

Laravel Eloquent - $fillable is not working?

I have set the variable $fillable in my model. I wanted to test the update functionality, and I get this error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column '_method' in 'field list' (SQL: update positions set name = Casual Aquatic Leader, _method = PUT, id = 2, description = Here is my description, updated_at = 2014-05-29 17:05:11 where positions.client_id = 1 and id = 2)"
Why is this yelling at _method when my fillable doesn't have that as a parameter? My update function is:
Client::find($client_id)
->positions()
->whereId($id)
->update(Input::all());
Change following:
->update(Input::all());
to this (exclude the _method from the array)
->update(Input::except('_method'));
Update:
Actually following update method is being called from Illuminate\Database\Eloquent\Builder class which is being triggered by _call method of Illuminate\Database\Eloquent\Relations class (because you are calling the update on a relation) and hence the $fillable check is not getting performed and you may use Input::except('_method') as I answered:
public function update(array $values)
{
return $this->query->update($this->addUpdatedAtColumn($values));
}
If you directly call this on a Model (Not on a relation):
Positions::find($id)->update(Input::all());
Then this will not happen because fillable check will be performed within Model.php because following update method will be called from Illuminate\Database\Eloquent\Model class:
public function update(array $attributes = array())
{
if ( ! $this->exists)
{
return $this->newQuery()->update($attributes);
}
return $this->fill($attributes)->save();
}
write a parent class
class BaseModel extends Model
public static function getFillableAttribute(Model $model, $data){
$array = $model->getFillable();
$arr = [];
foreach ($array as $item){
if( isset($data["$item"])){
$arr["$item"] = $data["$item"];
}
}
return $arr;
}
I experienced this breaking after updating from Laravel 5.2 to 5.4 - I can't find anything in the documentation / migration guide that covers it.
As covered in this github issue the correct fix/use of Eloquent seems to be:
Positions::find($id)->fill(Input::all())->save();
To trigger the fillable check by laravel and then perist the changes.
You could also do this
$data = request()->all();
//remove them from the array
unset($data['_token'],$data['_method']);
//then
Client::find($client_id)
->positions()
->whereId($id)
->update($data);
This removes the _method and _token from the array

Resources