Laravel dates on form model binding - php

I'm using Laravel 5.1
I got a model:
class ExampleModel extends Model {
// ....
protected $dateFormat = 'Y.m.d';
protected $dates = ['first_date', 'second_date'];
// ...
}
So when I'm indexing ExampleModel elements, the date format is correct (ex 2015.07.31)
But on an edit form it uses the default format: 2015-07-31 00:00:00
I'm using Form::model() binding.
I know I could use getFirstDateAttribute() but it's not the solution I'm looking for. Because it's not elegant at all and once I defined the $dates array, it should work automatically in every case.
So is it a bug maybe? Or am I doing something wrong?

I solved my problem by overriding Carbon's default date format:
Carbon\Carbon::setToStringFormat('Y.m.d');
But Kelly's answer is much better and more elegant, I just post this one as well, maybe someone will find this useful once.

I've never done this before, but it seems to work on a basic example I put together. Note that I'm just calling the toArray method on the model in the form opening tag.
{!! Form::model($exampleModel->toArray(), ['route' => ['example-models.update', $exampleModel->id]]) !!}
{!! Form::label('first_date', 'First Date') !!}
{!! Form::text('first_date') !!}
{!! Form::close() !!}
The docs say that the dateFormat property determines the date format when the object is cast to json or an array.

I don't fully advise this as a fix because when you update the core you'll lose this fix, but maybe this should be posted as a pull request to Laravel's next version.
In \Illuminate\Database\Eloquent\Concerns\HasAttributes update the asDate function as follows. As you can see from the comments the asDate function still returns a timestamp even though it's 00:00:00.
/**
* Return a timestamp as DateTime object with time set to 00:00:00.
*
* #param mixed $value
* #return \Illuminate\Support\Carbon
*/
protected function asDate($value)
{
$date = $this->asDateTime($value)->startOfDay();
$date->setToStringFormat($this->getDateFormat());
return $date;
}
This allows you to control the format of a date from the model (note I'm differentiating between a date and datetime). Then use the casts variable to cast your variable to date format.
protected $casts = [
'start_date' => 'date',
'end_date' => 'date'
];
protected $dateFormat = 'Y-m-d';
The $dates variable cannot differentiate between a date and a datetime.
UPDATE
The real problem is the form model binding skips the helper function of the FormBuilder class. As you can see if you inspect \Collective\Html\FormBuilder::date
/**
* Create a date input field.
*
* #param string $name
* #param string $value
* #param array $options
*
* #return \Illuminate\Support\HtmlString
*/
public function date($name, $value = null, $options = [])
{
if ($value instanceof DateTime) {
$value = $value->format('Y-m-d');
}
return $this->input('date', $name, $value, $options);
}
It is correctly formatting date to 'Y-m-d' as specified in HTML5 spec. However at this point $value is actually null. So you have to update the generic input function.
/**
* Create a form input field.
*
* #param string $type
* #param string $name
* #param string $value
* #param array $options
*
* #return \Illuminate\Support\HtmlString
*/
public function input($type, $name, $value = null, $options = [])
{
...
//$value is null here
if (! in_array($type, $this->skipValueTypes)) {
//$value is fetched based on hierarchy set by
$value = $this->getValueAttribute($name, $value);
//necessary duplicate code to format date value
if ($type == 'date' && $value instanceof DateTime) {
$value = $value->format('Y-m-d');
}
}
...
}
UPDATE There is no need to update vendor code. Check out FormModelAccessors
https://laravelcollective.com/docs/5.2/html#form-model-binding

Related

9 Digit Time to Hours, Minutes and Seconds

I'm coding an integration to a particular software that is not open-source and i have come across a column in the database called "TIME_" which is a 9-digit integer. This time represents Hours-Minutes-Seconds without years, months or days. There is a different column for that, which is called "DATE_". DATE_ columns format is this "2022-01-01 00:00:00". It does not hold hours, minutes, or seconds. Like i said TIME_ column is used for that and i've been trying solutions to get a certain time format from a 9-digit integer but i couldn't come up with anything that worked. By the way, on the UI you can see the converted format of the column "TIME_" but it doesn't say how it converts it and i don't have access to the source code. Here are the solutions i tried:
First I thought it was a time in milliseconds so i wrote a function that takes one parameter that is time in milliseconds and returns a formatted hours, minutes, and seconds but the output wasn't anywhere near what it said on the UI. "271399244" was the data in the column "TIME_" and my function returned "75:23:19" and on the UI it said "16:45:57".
Then i assumed it was an epoch and tried every epoch conversion function in php and they didn't return anywhere near the right format.
Then did some maths and took the value of TIME_ column from 2 data rows and calculated the difference then went to the UI and took the converted value of those 2 rows and also calculated the difference between those values. Then tried to find a correlation but it wasn't consistent at all. So i ran out of solutions.
Screenshots below for a more detailed description.
On the UI:
In the database rows in respective order:
EDIT: As i mentioned in the question, I do not have access to the source code. I don't know how the conversion is being made from 9-digit integer to hours-minutes-seconds as it is my goal to find out.
Date and time components will typically ask for the date format to be something like YYYy-MM-DD, so in the model, you cast that value to date then the input should start rendering the value on page load if that is the issue.
I could have a Note model
class Note extends Model
{
protected $fillable = [
'text',
'is_private',
'userable_type',
'userable_id',
'scope',
'created_date',
];
protected $casts = [
'is_private' => 'boolean',
'created_date' => 'date',
];
}
Using the date cast - https://laravel.com/docs/9.x/eloquent-mutators#attribute-casting
Then in the input, you can do something like
<DateComponent value="{{ $note->date }}" />
Sorry if I'm off base but thats what I understand the question to be.
If your having the same problem with the time format you can create a custom cast to convert the time from epoch and back if need
https://laravel.com/docs/9.x/eloquent-mutators#custom-casts
Could also be related to this issue, this link might help write a custom cast
How to convert a date YYYY-MM-DD to epoch in PHP
That might look something like this.
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class TimeCast implements CastsAttributes
{
/**
* Cast the given value.
*
* #param \Illuminate\Database\Eloquent\Model $model
* #param string $key
* #param mixed $value
* #param array $attributes
* #return mixed
*/
public function get($model, $key, $value, $attributes)
{
// Could also use strtotime
return gmdate("H:i:s", $value / 1000);
}
* Prepare the given value for storage.
*
* #param \Illuminate\Database\Eloquent\Model $model
* #param string $key
* #param array $value
* #param array $attributes
* #return mixed
*/
public function set($model, $key, $value, $attributes)
{
return strtotime($value) * 1000;
}
}

Change automatically the format of date fields (Laravel)

I'm looking for a DateTime Mutator that change the format of dates, I'm working with Oracle DB and the admitted Format is (DD/MM/YYYY) and the input type "date" stores dates in (YYYY,MM,DD) format.
I found the $date function and a trait by Torzer, but I have to indicate the fields that I want to convert the format.
is there some trait or function that detect all date fields and convert them automatically in a format (DD/MM/YYYY)? this without indicate the field.
nowadays I use protected $date in my model:
protected $dates = [ 'fecha_nac', 'fecha_nac1', 'fecha_nac2', ];
By default laravel uses date formate 'Y-m-d H:i:s' if you want to use a different format you can customize it in your model in the following way.
protected $dateFormat = 'your date formate';
in your case it will be.
protected $dateFormat = 'd-m-Y';
You can override the getDates method on HasAttributes trait.
/**
* Get the attributes that should be converted to dates.
*
* #return array
*/
public function getDates()
{
$defaults = [static::CREATED_AT, static::UPDATED_AT];
return $this->usesTimestamps()
? array_unique(array_merge($this->dates, $defaults))
: $this->dates;
}
On your model:
public function getDates()
{
$dates = parent::getDates();
// add your dynamic logic here
return $dates;
}
I would really go for explicitly defining which fields should be converted as these dynamic operations can be expensive if you are working with the model quite a lot.

Get The Time From Database in Symfony 2

I have a field in database (Time) with this value 09:00:00.
I created the Entity and with Time Field
/**
* #var \DateTime
*
* #ORM\Column(name="m_01_ch", type="time")
*/
private $m_01_ch;
In my controller I retrieve the element and when I do:
$val = $myentity->getM01Ch();
My value is (in XDebug)
$val = {DateTime}[3]
date= "2015-07-08 09:00:00.000000"
timezone_type = 3
timezone "Europe/Rome"
If I get $val->date I have All the Date, but I want to get only 09:00:00
Can I take my "original" value without use Regex etc?
You can modify your entity where you are returning your value. In your function getM01Ch() do something like this
/**
* #return \DateTime
*/
public function getM01Ch()
{
$returnValue = $this->m_01_ch->format('h:i:s')
return $returnValue
}
Other than that I don't know if any better approach exists. More info

Saving a Zend date in the database with Doctrine 2.1

I want to save a datetime in the database which was created with the doctrine schema tool.
In my form I set a date and time and i want to save it as a datetime in the database.
So i tried this:
$e->setStartDateTime(new Zend_Date('2011-09-01T22:00:00',Zend_date::DATETIME));
But i get the error:
PHP Fatal error: Call to undefined method Zend_Date::format() in /var/www/shared/Doctrine/lib/vendor/doctrine-dbal/lib/Doctrine/DBAL/Types/DateTimeType.php on line 44
Does anyone have experience with this and able to help me with this problem?
You can override the native datatypes to use Zend_Date instead of PHP's native DateTime which is the default for Doctrine data types 'datetime', 'time', and 'date'.
First in your application Bootstrap file, add the following BEFORE you instantiate your Doctrine EntityManager. This code should come before any other Doctrine code:
Doctrine\DBAL\Types\Type::overrideType('datetime', 'yournamespace\types\DateTimeType');
Doctrine\DBAL\Types\Type::overrideType('date', 'yournamespace\types\DateType');
Doctrine\DBAL\Types\Type::overrideType('time', 'yournamespace\types\Time');
Now you simply need to implement the 3 classes. It's easiest to just extend the corresponding Doctrine classes to achieve this. The code is actually the same for all 3 classes, the only difference is the class you extend from and the name of your class. Here is the DateTimeType class as an example:
namespace yournamespace\type;
use Doctrine\DBAL\Types\DateTimeType as DoctrineDateTimeType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* Override 'datetime' type in Doctrine to use Zend_Date
*/
class DateTimeType extends DoctrineDateTimeType
{
/**
* Convert from db to Zend_Date
*
* #param string $value
* #param AbstractPlatform $platform
* #return \Zend_Date|null
*/
public function convertToPhpValue($value, AbstractPlatform $platform)
{
if (is_null($value)) {
return null;
}
\Zend_Date::setOptions(array('format_type' => 'php', ));
$phpValue = new \Zend_Date($value, $platform->getDateTimeFormatString());
\Zend_Date::setOptions(array('format_type' => 'iso', ));
return $phpValue;
}
/**
* Convert from Zend_Date to db
*
* #param string $value
* #param AbstractPlatform $platform
* #return string|null
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if (is_null($value)) {
return null;
}
\Zend_Date::setOptions(array('format_type' => 'php', ));
$dbValue = $value->toString($platform->getDateTimeFormatString());
\Zend_Date::setOptions(array('format_type' => 'iso', ));
return $dbValue;
}
}
Now you can still use #Column(type="datetime") annotations in Doctrine. When saving to the database, you can save entity properties of type "datetime" to Zend_Date instances. Also when grabbing entities out of the database, properties of type "datetime" will now be Zend_Dates.
Doctrine2 expects PHP DateTime objects for DQL date and datetime types.
If you are not forced to use a Zend_Date, to this:
->setStartDateTime(new DateTime('2011-09-01T22:00:00'))
Else, convert it to a DateTime:
new DateTime('#' . $zendDate->getTimestamp())
See DateTime docs.
You can implement a Custom Mapping Type or use this ZendDateType implementation.
You may find this guide helpful.

Auto-completion for Zend Form Elements

When creating form elements with Zend (using Zend Studio for Eclipse), I'd like some auto completion or hints. Here's what I'm thinking. I'm sure these exist, but I don't know how to get them.
I type createElement and auto-completes gives me the signature createElement($type, $name). Great, I select it.
but when I try to set the $type I don't get any hints like DateTextBox or ValidationTextBox. Being new, I see how this can be useful. What do you do to remember all the options?
for the array of attributes like require, invalidMessage, I'd like to get a list of those to choose from, and/or auto-complete when I start typing one.
// Date field
$date = $this->createElement('DateTextBox', 'date',
array('require' => 'true', 'invalidMessage' => 'Invalid date format')
);
$date->setLabel('date')->setRequired(true);
You have few options to help yourself, without waiting for any plugin:
learn it and remember ;)
extend your phpDoc blocks with all available options:
Example (to be honest I don't know if Eclipse supports html in phpDoc or even any text after variable name in #param, but it works fine in Netbeans):
/**
* [...]
* #param string $type Can be: <ul><li>DateTextBox</li><li>ValidationTextBox</li></ul>
* #param string $name Whatever
* #param array|Zend_Config $options Array with following keys: <ul><li>require</li><li>invalidMessage</li></ul>
* #return Zend_Form_Element
*/
public function createElement($type, $name, $options = null)
extend Zend class and create your own methods to simplify your work
Example:
class My_Zend_Form_Element extends Zend_Form_Element
{
public function createDateTextBox($name, $options = null)
{
return $this->createElement('DateTextBox', $name, $options);
}
}
declare some well named constants and provide some hint in phpDoc
Example: (type ZFE_OPTIONS and IDE should show hint with some constants to use as array keys)
/**
* Can be true or false
*/
define('ZFE_OPTIONS_REQUIRE','require');
create your own helper classes with methods to produce valid options array
Example:
class ZFE_Options
{
protected $opts = array();
/**
* #param bool $req
* #return ZFE_Options
*/
public function setRequired($req){
$this->opts['require'] = (bool)$req;
return $this;
}
/**
* #param string $txt
* #return ZFE_Options
*/
public function setInvalidMessage($txt){
$this->opts['invalidMessage'] = (string)$txt;
return $this;
}
/**
* #return array
*/
public function toArray(){
return $this->opts;
}
}
$zfe_options = new ZFE_Options();
$opts = $zfe_options
->setRequired(true)
->setInvalidMessage('Please provide valid email address')
->toArray();
That's not possible. It's not how autocompletion works. The hints you get are taken directly from ZF's code documentation. Nothing more, nothing less. Everything you see as hints is taken directly from the DocBlock and method signature, e.g.
/**
* Create an element
*
* Acts as a factory for creating elements. Elements created with this
* method will not be attached to the form, but will contain element
* settings as specified in the form object (including plugin loader
* prefix paths, default decorators, etc.).
*
* #param string $type
* #param string $name
* #param array|Zend_Config $options
* #return Zend_Form_Element
*/
public function createElement($type, $name, $options = null)
Eclipse can tell you to insert a string or an array and it will know that the method returns a Zend_Form_Element, but it cannot tell you what these strings should be.
The only place where I know something like what you describe exists is for CSS files. For some reason, when I type in display: it will give me an autocomplete box with possible values for this declaration. If you want more sophisticated autocomplete like this, consider filing this as a feature request to Zend.

Categories