How to retrieve casted date without toArray or toJson? - laravel

For formatting some date column via casts, for example:
protected $casts = [
'deadline' => 'date:d/m/Y',
];
when getting column, it'll return carbon instance:
dd($model->deadline);
// Illuminate\Support\Carbon #1671235200 {#1542 ▶}
But even when it's casted to string, it won't be formatted as specified in cast:
dd( (string) $model->deadline );
// "2022-12-17 00:00:00"
Just when I can get formatted date, that whole model be casted toArray, or toJson,
dd($model->toArray()['deadline']);
// "17/12/2022"
So there isn't any easier way to get formatted date without casting whole model?

You can use a getter to overwrite your attribute :
public function getDeadlineAttribute()
{
return $this->deadline->format('d/m/Y');
}
If you want all your date formated that way for your model, you can use this :
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('d/m/Y');
}

You can add to your model a new getter function like this:
public function getFormatedDateAttribute()
{
return $this->deadline->format('d/m/Y');
}

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 Eloquent Save doesn't save

I am trying to save survey answers in my db, because of some to me unknown reason the ->save() method is not working, ->update() is working however.
I keep getting the error Array to string conversion every time I try to save.
I have used dd/return/var_dump/print_r whatever would work, to show that it was working up to that step. So now I know it works up to the ->save() method.
My controller:
$array = json_decode($request->getContent(), true);
foreach ($array as $survey) {
$objAns = new Survey_Answer();
$objAns->name = $survey['surveyName'];
$objAns->answers = $survey['answersPerQuestion'];
if($survey['complete'] === true) {
$objAns['complete'] = 1;
} else if($survey['complete'] === false) {
$objAns->complete = 0;
}
$objAns->save();
}
return;
My model:
class Survey_Answer extends Model
{
protected $fillable = ['name', 'answers', 'complete'];
}
My migration:
public function up()
{
Schema::create('survey__answers', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('survey_id')->unsigned()->nullable()->index();
$table->foreign('survey_id')->references('id')->on('surveys')->onDelete('cascade');
$table->string('name');
$table->json('answers');
$table->boolean('complete');
$table->timestamps();
});
}
I expect the code to save everything that I send along with the $request. This only results in a error: Array to string conversion.
Thank you very much for your help
You need to store your $survey['answersPerQuestion'] in json format not as an array.
$objAns->answers = json_encode($survey['answersPerQuestion']);
Although as #Lucas Arbex pointed out in the comments, there's certainly a better way to store it.
I would suspect that $survey['answersPerQuestion'] is an array but you are trying to store it in a json column.
You can use Laravel's Array & JSON Casting to cast the array to a json string.
The array cast type is particularly useful when working with columns
that are stored as serialized JSON. For example, if your database has
a JSON or TEXT field type that contains serialized JSON, adding the
array cast to that attribute will automatically deserialize the
attribute to a PHP array when you access it on your Eloquent model:
Your Model:
class Survey_Answer extends Model
{
protected $fillable = ['name', 'answers', 'complete'];
protected $casts = [
'answers' => 'array',
];
}
Once the cast is defined, you may access the options attribute and it
will automatically be deserialized from JSON into a PHP array. When
you set the value of the options attribute, the given array will
automatically be serialized back into JSON for storage:
$user = App\User::find(1);
$options = $user->options;
$options['key'] = 'value';
$user->options = $options;
$user->save();

Laravel Models - Casting dates to Carbon instances

In a Laravel model it is possible to casts fields as Carbon dates like so:
class Example extends Model
{
protected $casts = [
'date_of_birth' => 'date'
]
}
However this can also be done like so:
class Example extends Model
{
protected $dates= [
'date_of_birth'
]
}
What is the difference and what is the recommended way of doing this?
Actually, in both cases, Laravel uses Model::asDateTime method. For example, when casting, it uses the following code for dates:
// ...
case 'date':
case 'datetime':
return $this->asDateTime($value);
Also, when getting an attribute from the model, the Laravel framework uses something like the following for dates (Model::getAttributeValue):
// ...
// If the attribute is listed as a date, we will convert it to a DateTime
// instance on retrieval, which makes it quite convenient to work with
// date fields without having to create a mutator for each property.
if (in_array($key, $this->getDates()) && ! is_null($value)) {
return $this->asDateTime($value);
}
So, there's no difference. Which way is the appropriate? It depends on your preference, IMO.

laravel 5 model method not working

I have a table with rows with column contain string "this\dIs\dA\dString"
$callPlans = CustomerCallPlan::where('customer_id', $customer->id)->get();
I get the values like above and expected string 'thisXIsXAXString'
as you guess I replace '\d' with 'X'. to do this I use method below inside model class.
class CustomerCallPlan extends Model
{
protected $table = 'customer_callplan';
protected $fillable = [
'template',
'priority',
'customer_id',
'strip',
'add_number',
'actiontype',
'data'
];
public function getNumbertemplateAttribute() {
return str_replace('\d', 'X', $this->attributes['template']);
}
}
But somehow data comes to model without replaced.. what might be cause this ??
This is called an accessor and it'll automatically be called by Eloquent when attempting to retrieve the value. The method name should be the camel cased name of the column you wish to access, prepended by get and followed by Attribute, for example getColumnNameAttribute() will take the column colum_name.

Resources