set date function with values from model in Yii2 - php

Hi i have function where i need to take values from dropdownlist(in 3 dropdownlists i have year, month, day like in facebook registration). I want to set date from this 3 values.
public function setDate()
{
$month=$this->month+1;
$date = date_create();
date_date_set($date, $this->year,$month, $this->day);
return date_format($date, 'Y-m-d');
}
then in my controller i want to save my date but it doesnt work.
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->setDate();
$model->save();
Which method of date can i use to this operation where i can give my values in parameters?
i try this method too
public function setDate()
{
$month=$this->month+1;
$date = new DateTime();
$date ->setDate($this->year,$month, $this->day);
return $date->format('Y-m-d');
}
but i have
Argument 1 passed to Faker\Provider\Base::__construct() must be an instance of Faker\Generator, none given, called in E:\htdocs\mesport\frontend\modules\settings\models\Profile.php on line 119 and defined

In your custom method you actually don't change the model attribute value. Assuming this method is located inside model, replace return line by:
$this->yourDateAttribute = ...;
Also make sure this attribute is safe in current scenario.

Related

Get DateTime object from Phalcon Model for DateTime column in PHP

In the database table there are some columns with DATETIME type. Is there any way I can get Phalcon Model to convert them automatically from string to DateTime object in PHP and vice versa without using beforeSave() and afterFetch()?
I tried to use annotation #Column(type='datetime'), but the data is still returned as string. And #Column(type='boolean') works fine for me though.
Could you please advise me on the right approach?
#Ярослав Рахматуллин is one way to go
or you can use phalcon setters and getters ( much simpler way )
check the documentation here
and this an example of the implementation for you situation to my understanding
use \DateTime;
class Foo extends \Phalcon\Mvc\Model
{
// set your column property to protected
protected $date;
// will be called when protected property $date is accessed
public function getDate()
{
// add your validation here to check if the date is not null
return new DateTime($this->date ?: 'now');
}
// will be called when protected property $date is set
public function setDate(DateTime $date = null)
{
// add your validation here to check if $date is not null
if (!is_object($date)) {
$date = new DateTime;
}
// this is mysql format for timestamp
$this->date = $date->format('Y-m-d H:i:s');
return $this;
}
}
// find first row
$foo = Foo::findFirst();
// check if the row exists
if ($foo) {
// $foo->date would return getDate()
var_dump($foo->date); // DateTime object
// this would call setDate()
// column date will convert DateTime object to the format Y-m-d H:i:s
$foo->date = new DateTime('2019-09-28'); // 2019-09-28 00:00:00
$foo->save();
}

Laravel scope using calculated value

I have the following problem in Laravel 5.4:
There´s a user table and a membership table, where a user can have many memberships, and a membership belongs to a user. The memberships are of annual duration, but a member will get an additional free day for each friend they recommend that registers on my site, thus the number of free days is constantly changing; this in turn changes the membership´s expiration date.
So the question is: How to scope the active memberships for a given user, if the expiration date is a variable?
I will need to do something like this:
First the expiration date in Membership.php:
This calculates the total days for each membership:
Note that the friendsDays are calculated per user in User.php
public function getTotalDaysAttribute() {
$days = $this->paidDays + $this->user->friendsDaysRemaining;
return $days;
}
This calculates the expiration date for each membership:
public function getExpirationDateAttribute() {
$date = $this->startDay->addDays($this->TotalDays);
return $date;
}
So far so good... Now, this is where I´m stuck (pseudo code):
public function scopeActive($query, $dateToCheck = Null) {
$query->where($dateToCheck >= $this->expirationDate);
}
How to code this properly to get:
dump($user->membership()->active()->get());
Thanks in advance.
You have two problems:
You are trying to use model values in your scope. Scopes happen before the model values have been set. This is because in order to get the values in the model to be set the query which fetches the data must first be executed.
Unfortunately due to your database design you won't be able to create a scope to get the answer you want. This is because you are using values on a different table to calculate your TotalDays value.
I suggest you change expirationDate value in the database and call it when a friend is invited.
Something like:
function addDaysToExpiration(User $user) {
$user->expirationDate = date('Y-m-d h:m:s', strtotime('2008-10-05' . '+1 day'));
$user->save();
}
You can pass variable to scope, so for example you can define scope like this:
public function scopeActive($query, \Carbon\Carbon $dateToCheck = Null)
{
$query->where('some_date_field', '>=' ($expirationDate ?? now())->toDateTimeString());
}
and then you can do:
$dateToCheck = now()->addDays(30);
dump($user->membership()->active($dateToCheck)->get());
You can also pass only number of days to scope instead of Carbon instance if it's more convienient to use in your case.
With the API you defined for yourself:
$user->membership()->active()->get();
Your method scopeActive won't be able to see related User and friendsDaysRemaining variable that you need for calculating the expiration date. You can try it for yourself:
public function scopeActive($query) {
var_dump($this->id); // null
var_dump($this->user); // null, this part will try to do the query: select * from users where id = null
}
In your position, I would probably go with a persisted expiration_date column on the memberships table and update it whenever needed. This would then allow you to do smth like:
public function scopeActive($query) {
return $query->where('expiration_date', '>', Carbon::now());
}
Thank you guys for your prompt answers. I figured it out using a different approach, based on your ideas. Since I cannot use calculated fields in the query, I went back to a field that do exists in the DB, this is the renewalDueDate, that´s a year from the payment date; both are known and fixed dates. Then, in the query I pass the $user and the $dateToCheck as parameters, substract the remaining friends days and compare to that value, like this:
public function scopeActive($query, $user, $dateToCheck = Null) {
// If no date is passed, use today()
$dateToCheck = is_null($dateToCheck) ? Carbon::today() : Carbon::parse($dateToCheck);
//Substract the friendsDaysRemaining from the dateToCheck
$AdjustedEndDate = $DateToCheck->copy()->subDays($user->friendsDaysRemaining);
//Build the query
$query ->where('paid', 1) //its been paid for
->where('startDay', '<=', $DateToCheck) //It has started
->where('renewalDueDate', '>=', $AdjustedEndDate); //It has not expired
return $query;
}
Although is cumbersome to have to pass the user to get the remaining friends days, this is now working fine:
$dateToCheck= '2018-09-01';
dump($user->membership()->active($user, $dateToCheck)->pluck('id'));
Result:
Collection {#299 ▼ #items: array:2 [▼
0 => 83
1 => 6 ] }
Of course you could also pass the $friendsDaysRemaining instead of the $user, but is also far from elegant.
Thanks again.

Validate Date and Time in the Laravel Model

I have a datepicker plugin to pop-up a calendar view to allow users to select a date + time, however the format which it produces is:
May 9, 2016 8:30 AM
When storing to the database, I need the format to be:
2016-09-05 08:30:00
In the controller of my application, I have:
public function save(Request $request)
{
Entry::create($request->all());
return redirect('entries');
}
Which saves the users form input, however it doesn't save the datetime due to the incorrect format. I have tried creating a new function to format the date before entering it into the database.
public function formatDate($data)
{
$returnDate = DateTime::createFromFormat('Y-d-m G:i:s', $data);
return $returnDate->format('Y-d-m G:i:s');
}
However when I call the function from the save function, it says undefined function. Am I doing something wrong or what would be the correct way to achieve this?
You have to set the correct format for DateTime::createFromFormat(). Create from format means, you have to tell a pattern to match any information in the given date. For your date, the pattern is:
DateTime::createFromFormat('F j, Y g:i A', $data);
Here is a demo: https://eval.in/567629
A list of all format options: http://php.net/manual/en/function.date.php
Both functions are inside the model?
If so, how are you trying to call the formatDate function?
You could use an anonymous function instead, try:
$formatted_date = function() use ($data) {
$returnDate = DateTime::createFromFormat('Y-d-m G:i:s', $data);
return $returnDate->format('Y-d-m G:i:s');
};
Inside your controller or model.
In Laravel, the created_at and updated_at are casted to Carbon objects
https://laravel.com/docs/5.2/eloquent-mutators#date-mutators
With your date, you could do the same thing
class User extends Model
{
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = ['created_at', 'updated_at', 'your_date'];
}
then, when saving the date, it will be cast to the correct format.
Give that a try and let us know how you get on!
For example;
$data = "May 9, 2016 8:30 AM";
return date('Y-d-m H:i:s',strtotime($data));

Convert String to Carbon

I am using Laravel 5.1
Few days ago I used protected $dates = ['license_expire'] in my model to convert the string date to Carbon instances. In HTML the default value in create form for the date was Carbon\Carbon::now()->format('Y-m-d')
In order to show alert in home page i used <p>Licence Expired: <b>{{ $employee->license_expire < Carbon\Carbon::now()?'License has expired':$employee->license_expire->diffForHumans() }}</b></p>
Till then diffForHumans() method works fine.
But in that case the edit form's default value also was today's date no matter what was in database(I am using a partial form). To resolve it I change the default value in HTML was NUll. And add another method in my model to show current date in create form.
public function getLicenseExpireAttribute($date)
{
return Carbon::parse($date)->format('Y-m-d');
}
After that when i go to home page i have an FatalErrorException which says Call to a member function diffForHumans() on string
When I check the date with dd($employee->license_expire) it become STRING again.
Can anybody tell me how can I convert the string to Carbon in this situation?
or
Make my create form's default date as today's date, the edit form's date from database and I can use diffForHumans() to show alert in home page?
You were almost there.
Remove protected $dates = ['license_expire']
and then change your LicenseExpire accessor to:
public function getLicenseExpireAttribute($date)
{
return Carbon::parse($date);
}
This way it will return a Carbon instance no matter what.
So for your form you would just have $employee->license_expire->format('Y-m-d') (or whatever format is required) and diffForHumans() should work on your home page as well.
If you're using Laravel 9+, you can alternatively use the updated syntax for defining Accessors :
use Illuminate\Database\Eloquent\Casts\Attribute;
public function licenseExpire(): Attribute
{
return Attribute::make(
get: fn ($value) => Carbon::parse($value);
);
}
Why not try using the following:
$dateTimeString = $aDateString." ".$aTimeString;
$dueDateTime = Carbon::createFromFormat('Y-m-d H:i:s', $dateTimeString, 'Europe/London');
$filter['dateOfService']='06.2021';
$d1 = Carbon::createFromFormat('m.Y', $filter['dateOfService'], 'Europe/Warsaw')->format('m.Y');
Try this
$date = Carbon::parse(date_format($youttimestring,'d/m/Y H:i:s'));
echo $date;

Multiple Timezoning in Laravel/Carbon

I was wondering if this was possible, so let's say I have a model like so:
MyModel
SomeDate - Carbon
Now, I also have a timezone for the current user like so:
User
MyTimezone
the timezones stored in the database are always stored in UTC format (to ensure everything is consistent), and the outputted dates should always be formatted to a specific Timezone (but timezone differs per user), for example America/Chicago for User1 and America/Denver for User2.
Is there a way to automatically format the timezones per Carbon instance to a given one before outputting, or will I have to loop through the collection and set each one accordingly?
Setting app.timezone doesn't work because it also causes Carbon instances to be saved to the database in the app.timezone timezone, whereas all dates in the database should be in UTC, therefore I lose consistency.
I currently have app.timezone set to UTC in the App config but I'm also forced to convert all Carbon instances to the correct timezone before outputting. Is there a better way, maybe by trapping execution before Carbon gets turned into a string and doing it there?
EDIT:
Things i've tried:
Override setAttribute & getAttribute:
public function setAttribute($property, $value) {
if ($value instanceof Carbon) {
$value->timezone = 'UTC';
}
parent::setAttribute($property, $value);
}
public function getAttribute($key) {
$stuff = parent::getAttribute($key);
if ($stuff instanceof Carbon) {
$stuff->timezone = Helper::fetchUserTimezone();
}
return $stuff;
}
overriding asDateTime:
protected function asDateTime($value)
{
// If this value is an integer, we will assume it is a UNIX timestamp's value
// and format a Carbon object from this timestamp. This allows flexibility
// when defining your date fields as they might be UNIX timestamps here.
$timezone = Helper::fetchUserTimezone();
if (is_numeric($value))
{
return Carbon::createFromTimestamp($value, $timezone);
}
// If the value is in simply year, month, day format, we will instantiate the
// Carbon instances from that format. Again, this provides for simple date
// fields on the database, while still supporting Carbonized conversion.
elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $value))
{
return Carbon::createFromFormat('Y-m-d', $value, $timezone)->startOfDay();
}
// Finally, we will just assume this date is in the format used by default on
// the database connection and use that format to create the Carbon object
// that is returned back out to the developers after we convert it here.
elseif ( ! $value instanceof DateTime)
{
$format = $this->getDateFormat();
return Carbon::createFromFormat($format, $value, $timezone);
}
return Carbon::instance($value);
}
Running into the same issue for my application where remote websites would store dates in UTC and I'd have to show the actual dates based on the logged in user, I came up with overriding the Laravel Eloquent Model.
Just extend the Illuminate\Database\Eloquent\Model, like so:
<?php namespace Vendor\Package;
use Illuminate\Database\Eloquent\Model as EloquentModel;
class Model extends EloquentModel
{
/**
* Return a timestamp as a localized DateTime object.
*
* #param mixed $value
* #return \Carbon\Carbon
*/
protected function asDateTime($value)
{
$carbon = parent::asDateTime($value);
// only make localized if timezone is known
if(Auth::check() && Auth::user()->timezone)
{
$timezone = new DateTimeZone(Auth::user()->timezone);
// mutates the carbon object immediately
$carbon->setTimezone($timezone);
}
return $carbon;
}
/**
* Convert a localized DateTime to a normalized storable string.
*
* #param \DateTime|int $value
* #return string
*/
public function fromDateTime($value)
{
$save = parent::fromDateTime($value);
// only make localized if timezone is known
if(Auth::check() && Auth::user()->timezone)
{
// the format the value is saved to
$format = $this->getDateFormat();
// user timezone
$timezone = new DateTimeZone(Auth::user()->timezone);
$carbon = Carbon::createFromFormat($format, $value, $timezone);
// mutates the carbon object immediately
$carbon->setTimezone(Config::get('app.timezone'));
// now save to format
$save = $carbon->format($format);
}
return $save;
}
}
Perhaps this is useful for others stumbling upon this question.
As a reference
laravel 5 (2015-03-18): Illuminate\Database\Eloquent\Model:2809-2889
laravel 4.2 (2015-03-18): Illuminate\Database\Eloquent\Model:2583-2662
If I understand correctly, what you are trying to achieve is to convert timezone from A format to B format and send it to the user, where A format is stored in database and B format is converted to after retrieving records from database.
Here is a neat way to do that.
In the models such as User and MyModel where conversion is needed, add a function in model:
public function getConversionAttribute()
{
$conversion = Convert($this->SomeDate);
//Convert is the customized function to convert data format
//SomeDate is the original column name of dates stored in your database
return $conversion;
}
Now if you query User model or MyModel using $user = User::find(1), you can now get the converted date by accessing the conversion attribute using $user->conversion.
Cheers!
However, attribute added this way will not included in converted array. You need to add another function in your model.
public function toArray()
{
$array = parent::toArray();
//if you want to override the original attribute
$array['SomeDate'] = $this->conversion;
//if you want to keep both the original format and the current format
//use this: $array['Conversion'] = $this->conversion;
return $array;
}
General Version:
public function toArray() {
$array = parent::toArray();
//if you want to override the original attribute
$dates = $this->getDates();
foreach ($dates as $date) {
$local = $this->{$date}->copy();
$local->timezone = ...
$array[$date] = (string)$local;
}
//if you want to keep both the original format and the current format
//use this: $array['Conversion'] = $this->conversion;
return $array;
}

Categories