Saving a Zend date in the database with Doctrine 2.1 - php

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.

Related

API Platform "Unexpected non-object value for object property."

I am trying to use API Platform serialization with calculated field as in here https://api-platform.com/docs/core/serialization/#calculated-field
Here is the code
/**
* #Groups({
* "read:actionJeu"
* })
*/
public function getTimePassed(){
return 4;
}
The normalization context is normalizationContext={"groups"={"read:actionJeu"}, "enable_max_depth"=true}
The problem is that when I do return 4, it shows this error
But when I change the return to something else (for example return new \DateTime('now') ), I get it working.
I wonder why this is happening, I tried with string too, but it doesn't work.
I supose that your entity property $timePassed is type of DateTime. Return type of method getTimePassed() also must be the DateTime type. You can change type of property $timePassed to int instead of DateTime or make your custom TimePassedSerializer service.
/** this **/
private int $timePasede;
/** insted of this **/
private DateTime $timePasede
It might be caused by the API Platform core package version.
Try installing version 2.6.8 or 2.6.6.
I was dealing with the same thing on the 2.6.7.
The example defines the return type of the function with : int at the end, so that it's clear that this function will return an integer.
I guess that api-platform will expect an object per default until a different return type has been defined.
So this should work from my understanding. Note that I haven't tested it because I don't have the possibility for it right now.
/**
* #Groups({
* "read:actionJeu"
* })
*/
public function getTimePassed(): int {
return 4;
}

Symfony doctrine timestamp field

I'm trying to create a timestamp database field type for my Symfony project.
I have created the following database type:
class TimestampType extends Type {
const TIMESTAMP_TYPE_NAME = 'timestamp';
/**
* Gets the SQL declaration snippet for a field of this type.
*
* #param array $fieldDeclaration The field declaration.
* #param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform.
*
* #return string
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return "TIMESTAMP";
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value;
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $value;
}
/**
* Gets the name of this type.
*
* #return string
*
* #todo Needed?
*/
public function getName()
{
return self::TIMESTAMP_TYPE_NAME;
}
}
In my entity, I have declared the following property:
/**
* #var \DateTime
* #ORM\Column(name="created", type="timestamp", options={"default":"CURRENT_TIMESTAMP"})
*/
protected $created = null;
It all looks good, but when running a database update, I get an error:
An exception occurred while executing 'ALTER TABLE question CHANGE created created TIMESTAMP DEFAULT 'CURRENT_TIMESTAMP' NOT NULL COMMENT '(DC2Type:timestamp)'':
SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'created'
For some reason, my default value is being encapsulated in single quotes. This doesn't happen for datetime fields, but then I get an error the default value is invalid.
Is there any way I can make Symfony accept a timetamp field with CURRENT_TIMESTAMP as default value?
I've tried the following in my custom type, by commenting out the appending query Symfony adds:
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '(DC2Type:" . self::TIMESTAMP_TYPE_NAME . ")' #--";
}
That works, but now Symfony always thinks it needs to update my tables and runs the query for every table that it thinks it needs to update.
My goal is to have a timestamp in the database if I run native insert queries. I know it can be done using HasLifecycleCallbacks and I have them configured, but I want to avoid ORM at some points and use native queries.
Any help would be appreciated. :)
A funny little trick I've seen is this (you wouldn't need the database type you created, just update the mapping):
/**
* #ORM\Column(type="datetime", nullable=false)
* #ORM\Version
* #var \DateTime
*/
protected $created = null;
What happens behind the scenes is that Doctrine will end up casting the datetime to a timestamp since it was combined with #version, and should add the default timestamp schema change.
With that said, this isn't quite the intended use for this feature (http://doctrine-orm.readthedocs.org/en/latest/reference/annotations-reference.html#annref-version), and I'd be curious what happens on subsequent update queries you make.
I know you're looking for the default to be set in MySQL so you can run queries outside of Doctrine, but the clearest way to add a default timestamp for me has always been to add it in the object's constructor:
public function __construct()
{
$this->created = new \DateTime();
}

PHPStorm Type hinting array of different types

Is it possible in PHPStorm to type hint an array with different object types, ie:
public function getThings()
{
return array (new Thing(), new OtherThing(), new SomethingElse());
}
Even declaring them separately before building the array doesn't seem to work.
you can use phpdocs in order for phpstorm to accept an array of multiple types like so:
/**
* #return Thing[] | OtherThing[] | SomethingElse[]
*
*/
public function getThings()
{
return array (new Thing(), new OtherThing(), new SomethingElse());
}
This technique will make phpstorm think that the array could contain any of those objects and so it will give you type hinting for all three.
Alternatively you can make all of the objects extend another object or implement an interface and type hint that once object or interface like so:
/**
* #return ExtensionClass[]
*
*/
public function getThings()
{
return array (new Thing(), new OtherThing(), new SomethingElse());
}
This will give you type hints for only what the classes extend or implement from the parent class or interface.
I hope this helped!
This is described in the PHPDoc standards
https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#713-param
/**
* Initializes this class with the given options.
*
* #param array $options {
* #var bool $required Whether this element is required
* #var string $label The display name for this element
* }
*/
public function __construct(array $options = array())
{
<...>
}
In PHP, I've seen a very nice way of doing this:
#return array<Thing,OtherThing,SomethingElse>
IDEs like PHPStorm and VSCode understand this syntax pretty well. Hope this helps.

Symfony2 doesn't persist all the columns in a table

I have a entity with several fields. Now I have added to my database a new column "date" that is a datetime object.
But when I add a new register to the database this field always have value null, never caught the value that I put in the form.
The entity have the correct values, but if I saw all the values, the entity manager has a variable called "SelectColumnListSQL", and in this SQL action, doesn't appear the field "date".
The logs doesn't write any error, only store in my database the rest of the fields ok but this not.
If hay use dev enviromnent, in this case all works right :S
Any idea??
--- Entity Info ---
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime", nullable=false)
*/
protected $date;
/**
* Set date
*
* #param \DateTime $date
* #return Quotes
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
Thanks!
Please, concrete which ORM are you using (Doctrine, Propel...)
Have you already run the following commands?
php app/console doctrine:generate:entities
php app/console doctrine:schema:update
You must include the info in your Entity dir in your Bundle
I am pretty sure that you forget one setter method or the method is misspelled.
See the example below for more details.
Assume that you have this entity
class Entity{
// ...
private field;
// the getter and setter methods
public function getField(){
// ...
}
public function setField(){ // I guess this function is missing or misspelled
// ...
// If the function is missing or misspelled,
// doctrine will not take into account the changes you did for that field
}
}
best,
Check that you have totaly updated your database by this command:
php app/console doctrine:schema:update --dump-sql

Zend Framework Model, Active Record pattern alternative

I'm writing some code that allows users to read reports on a site, using AJAX calls to dynamically load only what is requested, instead of the entire 15+MB report.
I'm writing a Model to access all the report data from the database, and I don't want to use the Active Record pattern. I'm following the idea of "A Model HAS a table, instead of IS-A table", since this model will be accessing 5 different tables, and there are some complex MySQL JOIN's between these tables.
What is a good design pattern to follow in Zend Framework for this, examples?
UPDATED on 2012-12-05 # 12:14PM EST
I'm currently working for a Market Research Report company. Without using actual function names, or revealing any meaningful details of the code, here are the basics:
readreportAction() does:
get the report meta data
get the report "table of contents"
readsectionAction() does:
get the report text, only a part of it
get the embedded tabular data
get the figures / images
get the footnotes
format the report text
reportpdfAction() does the exact same thing as readreportAction() and readsectionAction(), except all at one time. I'm trying to conceptualize a way to NOT copy + paste this code / programming logic. A data mapper seems to solve this.
I would recommend the Data Mapper pattern.
Everything you said makes sense and this pattern fits. Your model should not know or care how it is persisted. Instead the mapper does what it suggests - maps your model to your database. One of the things I like about this approach is it encourages people to think about the model in terms of an object, not a relational database table, as often happens with active record patterns and table row gateways.
Your object, unless very simple, typically will not reflect the structure of a database table. This lets you write good objects and then worry about the persistence aspects afterward. Sometimes more manual in that your mapper will need to deal with the complex joins, probably requiring writing some code or SQL, but the end result is it does just what you want and nothing more. No magic or conventions required if you don't want to leverage them.
I've always though these articles do a good job of explaining some of the design patterns that can be used well in ZF: http://survivethedeepend.com/zendframeworkbook/en/1.0/implementing.the.domain.model.entries.and.authors#zfbook.implementing.the.domain.model.entries.and.authors.exploring.the.entry.data.mapper
UPDATE:
Well you mapper might extend from an interface similar to this:
<?php
interface Mapper_Interface
{
/**
* Sets the name of the entity object used by the mapper.
*/
public function setObjectClass($class);
/**
* Sets the name of the list class used by the mapper.
*/
public function setObjectListClass($listClass);
/**
* Get the name of the object class used by the mapper.
*
*/
public function getObjectClass();
/**
* Get the name of the object list class used by the mapper.
*
* #return string
*/
public function getObjectListClass();
/**
* Fetch one row.
*
* #param array $where Criteria for the selection.
* #param array [$order = array()] Optionally the order of results
* #return Object_Abstract
* #throws Mapper_Exception
*/
public function fetchRow($where, $order = array());
/**
* Fetch all records. If there is no underlying change in the persisted data this should
* return a consistant result.
*
* #param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
* #param string|array $order OPTIONAL An SQL ORDER clause.
* #param int $count OPTIONAL An SQL LIMIT count.
* #param int $offset OPTIONAL An SQL LIMIT offset.
* #return Object_List_Abstract
* #throws Mapper_Exception
*/
public function fetchAll($where = null, $order = null, $count = null, $offset = null);
/**
* Deletes one or more object.
*
* #param array|string $where Criteria for row deletion.
* #return integer $affectedRows
* #throws Mapper_Exception
*/
public function delete($where);
/**
* Saves a record. Either updates or inserts, as required.
*
* #param $object Object_Abstract
* #return integer $lastInsertId
* #throws Mapper_Exception
*/
public function save($object);
}
And you would interact with the mapper like:
$fooObjectMapper = new Foo_Mapper;
$fooObjectList = $fooObjectMapper->fetchAll();
var_dump($fooObjectList->first());
or
$fooObjectMapper = new Foo_Mapper;
$fooObject = $fooObject->fetch(array('id = ?' => 1));
$fooObject->setActive(false);
$fooObjectMapper->save($fooObject);
I usually write a mapper abstract for any 'PDO' enabled databases. One of the attributes of that concrete mapper is then the Zend_Db_Adapter to issue commands against. Makes for a flexible solution, easy to use mock data sources in testing.
First it looks like you need to make a little bit more of a conceptual leap. With the data mapper pattern it helps to think in terms of objects instead of database tables. I found these two articles helpful when I needed to make the leap.
http://phpmaster.com/building-a-domain-model/
http://phpmaster.com/integrating-the-data-mappers/
That being said ZF 1 has some very useful tools for building a data mapper/domain model.
The convention in ZF 1 is for each table you are working with to be accessible through the Zend_Db_Table api. The simplest way I've found is to just use the DbTable resource for each table. You could also use the Zend_Db::factory or new Zend_Db_Table('tableName') or any other method that appeals to you.
This example is based on a mp3 song track.
//in effect this is the database adapter for database table 'track', This is $tableGateway used later.
<?php
class Application_Model_DbTable_Track extends Zend_Db_Table_Abstract
{
//name of database table, required to be set if name of class does not match name of table
protected $_name = 'track';
//optional, column name of primary key
protected $_primary = 'id';
}
there are several ways to attach a table to the Db adapter and the Zend_Db_Table api, I just find this method simple to implement and it makes setting up a mapper simple as well.
The mapper class is the bridge between the data source and your object (domain entity). The mapper interacts with the api for Zend_Db_Table in this example.
A really important point to understand: when using classes that extend Zend_Db_Table_Abstract you have all the basic functionality of the Zend_Db component at your disposal. (find(),fetchall(), fetchRow(), select() ...)
<?php
class Music_Model_Mapper_Track extends Model_Mapper_Abstract
{
//the mapper to access the songs artist object
protected $artistMapper;
//the mapper to access to songs album object
protected $albumMapper;
/**
* accepts instance of Zend_Db_Table_Abstract
*
* #param Zend_Db_Table_Abstract $tableGateway
*/
public function __construct(Zend_Db_Table_Abstract $tableGateway = null)
{
//at this point I tend to hardcode $tablegateway but I don't have to
$tableGateway = new Application_Model_DbTable_Track();
parent::__construct($tableGateway);
//parent sets the $tablegateway variable and provides an abstract requirement
//for createEntity(), which is the point of this class
}
/**
* Creates concrete object of Music_Model_Track
*
* #param object $row
* #return Music_Model_Track
*/
public function createEntity($row)
{
$data = array(
'id' => $row->id,
'filename' => $row->filename,
'format' => $row->format,
'genre' => $row->genre,
'hash' => $row->hash,
'path' => $row->path,
'playtime' => $row->playtime,
'title' => $row->title,
'track_number' => $row->track_number,
'album' => $row->album_id,//foriegn key
'artist' => $row->artist_id//foriegn key
);
//instantiate new entity object
return new Music_Model_Track($data);
}
/**
* findById() is proxy for find() method and returns
* an entity object.
*
* #param type $id
* #return object Model_Entity_Abstract
*/
public function findById($id)
{
//instantiate the Zend_Db_Select object
$select = $this->getGateway()->select();
$select->where('id = ?', $id);
//retrieve one database table row
$row = $this->getGateway()->fetchRow($select);
//create one entity object Music_Model_Track
$entity = $this->createEntity($row);
//return one entity object Music_Model_Track
return $entity;
}
//truncated
}
All that has gone before is for the express purpose of building the following object:
<?php
class Music_Model_Track extends Model_Entity_Abstract
{
/**
* $id, __set, __get and toArray() are implemented in the parent
*/
protected $album;
protected $artist;
protected $filename;
protected $format;
protected $genre;
protected $hash;
protected $path;
protected $playtime;
protected $title;
protected $track_number;
//artist and album mappers
protected $albumMapper = null;
protected $artistMapper = null;
//these are the important accessors/mutators because they convert a foreign key
//in the database table to an entity object.
public function getAlbum()
{
//if the album object is already set, use it.
if(!is_null($this->album) && $this->album instanceof Music_Model_Album) {
return $this->album;
} else {
//else we make a new album object
if(!$this->albumMapper) {
$this->albumMapper = new Music_Model_Mapper_Album();
}
//This is the album object we get from the id in our reference array.
return $this->albumMapper->findById($this->getReferenceId('album'));
}
}
//same as above only with the artist object.
public function getArtist()
{
if(!is_null($this->artist) && $this->artist instanceof Music_Model_Artist) {
return $this->artist;
} else {
if(!$this->artistMapper) {
$this->artistMapper = new Music_Model_Mapper_Artist();
}
return $this->artistMapper->findById($this->getReferenceId('artist'));
}
}
//the setters record the foriegn keys recorded in the table row to an array,
//this allows the album and artist objects to be loaded only when needed.
public function setAlbum($album)
{
$this->setReferenceId('album', $album);
return $this;
}
public function setArtist($artist)
{
$this->setReferenceId('artist', $artist);
return $this;
}
//standard setter and getters truncated...
}
so when using the track object you would get album or artist info like:
//this would be used in a controller most likely.
$mapper = new Music_Model_Mapper_Track();
$track = $mapper->findById('1');
//all of the information contained in the album or artist object is
//available to the track object.
//echo album title, year or artist. This album object also contains the artist object
//so the artist object would be available in two ways.
echo $track->album->title; //or
echo $track->artist->name;
echo $track->album->artist->name;
echo $track->getAlbum()->getArtist()->getName();
So what you really need to decide is how you want to structure your application. What I see as obvious may not be an option you wish to implement. A lot of the answers to your questions depend on exactly how these resources are to be used.
I hope this helps you at least a little bit.
You could consider using Doctrine 2. It's an ORM that does not use the ActiveRecord pattern.
In Doctrine, your models (entities) are all just normal PHP objects with zero knowledge of the database. You use mapping (xml, yaml or annotations) to tell Doctrine how they appear in the database, and the Entity Manager and repositories are used as a gateway for persisting entities or doing other database actions.

Categories