Hi Phalcon team,
I'm developing a webapp with Phalcon framework, i'm using the model annotations parser + model metadata set to customize my model property class.
But some problem appear and i did not fix it. For example:
I create a user manager feature with CRUD code. Read and Delete action is done and work okay, but Create and Update action is error, when add or edit the model appear message "The meta-data is invalid or is corrupt", can u help me fix my error, i'm tried change metadata storage from file to memory but it not work.
Machine develop enviroment: Mac OS X 10.10 Homebrew package
-
- Nginx 1.8
-
- PHP-FPM 5.5
-
- Phalcon 2.0.3
-
- MySQL 5.6
Bootstrap.php
public function initMetadata($options = [])
{
$config = $this->di['config'];
$this->di->set('modelsMetadata', function() use ($config) {
// $metaData = new PhMetadataFiles(
// ['metaDataDir' => $config->app_model->metadata]
// );
$metaData = new PhMetadataMemory();
//Set a custom meta-data database introspection
$metaData->setStrategy(new FlyAnnotationsMetaDataInitializer());
return $metaData;
});
}
public function initAnnotations($options = [])
{
$config = $this->di['config'];
$this->di->set('annotations', function() use ($config) {
// return new PhAnnotationsAdapter([
// 'annotationsDir' => $config->app_annotations->cache
// ]);
return new PhAnnotationsAdapterMemory();
});
}
AnnotationsInitializer.php
<?php
/**
* \Fly\AnnotationInitializer
* AnnotationInitializer.php
*
* Model Annotation Initializer class
*
* @author phalcon.io
* @since 2014-12-19
* @category Fly
*
*/
namespace Fly;
use Phalcon\Events\Event;
use Phalcon\Mvc\Model\Manager as ModelsManager;
use Phalcon\Mvc\ModelInterface;
class AnnotationsInitializer extends \Phalcon\Mvc\User\Plugin
{
/**
* This is called after initialize the model
*
* @param Phalcon\Events\Event $event
*/
public function afterInitialize(Event $event, ModelsManager $manager, ModelInterface $model)
{
//Reflector
$reflector = $this->annotations->get($model);
/**
* Read the annotations in the class' docblock
*/
$annotations = $reflector->getClassAnnotations();
if ($annotations) {
/**
* Traverse the annotations
*/
foreach ($annotations as $annotation) {
switch ($annotation->getName()) {
/**
* Initializes the model's source
*/
case 'Source':
$arguments = $annotation->getArguments();
$manager->setModelSource($model, $arguments[0]);
break;
/**
* Initializes Has-Many relations
*/
case 'HasMany':
$arguments = $annotation->getArguments();
$manager->addHasMany($model, $arguments[0], $arguments[1], $arguments[2]);
break;
/**
* Initializes Has-Many relations
*/
case 'BelongsTo':
$arguments = $annotation->getArguments();
if (isset($arguments[3])) {
$manager->addBelongsTo($model, $arguments[0], $arguments[1], $arguments[2], $arguments[3]);
} else {
$manager->addBelongsTo($model, $arguments[0], $arguments[1], $arguments[2]);
}
break;
}
}
}
}
}
AnnotationsMetaDataInitializer.php
<?php
/**
* \Fly\AnnotationsMetaDataInitializer
* AnnotationsMetaDataInitializer.php
*
* Metadata Annotation Initializer class
*
* @author phalcon.io
* @since 2014-12-19
* @category Fly
*
*/
namespace Fly;
use Phalcon\Db\Column;
use Phalcon\DiInterface;
use Phalcon\Mvc\Model\MetaData;
use Phalcon\Mvc\ModelInterface;
class AnnotationsMetaDataInitializer
{
/**
* Initializes the model's meta-data
*
* @param Phalcon\Mvc\ModelInterface $model
* @param Phalcon\DiInterface $di
* @return array
*/
public function getMetaData(ModelInterface $model, DiInterface $di)
{
$reflection = $di['annotations']->get($model);
$properties = $reflection->getPropertiesAnnotations();
if (!$properties) {
throw new Exception("There are no properties defined on the class");
}
$attributes = [];
$nullables = [];
$dataTypes = [];
$dataTypesBind = [];
$numericTypes = [];
$primaryKeys = [];
$nonPrimaryKeys = [];
$identity = false;
foreach ($properties as $name => $collection) {
if ($collection->has('Column')) {
$arguments = $collection->get('Column')->getArguments();
/**
* Get the column's name
*/
if (isset($arguments['column'])) {
$columnName = $arguments['column'];
} else {
$columnName = $name;
}
/**
* Check for the 'type' parameter in the 'Column' annotation
*/
if (isset($arguments['type'])) {
switch ($arguments['type']) {
case 'integer':
$dataTypes[$columnName] = Column::TYPE_INTEGER;
$dataTypesBind[$columnName] = Column::BIND_PARAM_INT;
$numericTypes[$columnName] = true;
break;
case 'string':
$dataTypes[$columnName] = Column::TYPE_VARCHAR;
$dataTypesBind[$columnName] = Column::BIND_PARAM_STR;
break;
}
} else {
$dataTypes[$columnName] = Column::TYPE_VARCHAR;
$dataTypesBind[$columnName] = Column::BIND_PARAM_STR;
}
/**
* Check for the 'nullable' parameter in the 'Column' annotation
*/
if (!$collection->has('Identity')) {
if (isset($arguments['nullable'])) {
if (!$arguments['nullable']) {
$nullables[] = $columnName;
}
}
}
$attributes[] = $columnName;
/**
* Check if the attribute is marked as primary
*/
if ($collection->has('Primary')) {
$primaryKeys[] = $columnName;
} else {
$nonPrimaryKeys[] = $columnName;
}
/**
* Check if the attribute is marked as identity
*/
if ($collection->has('Identity')) {
$identity = $columnName;
}
}
}
return [
//Every column in the mapped table
MetaData::MODELS_ATTRIBUTES => $attributes,
//Every column part of the primary key
MetaData::MODELS_PRIMARY_KEY => $primaryKeys,
//Every column that isn't part of the primary key
MetaData::MODELS_NON_PRIMARY_KEY => $nonPrimaryKeys,
//Every column that doesn't allows null values
MetaData::MODELS_NOT_NULL => $nullables,
//Every column and their data types
MetaData::MODELS_DATA_TYPES => $dataTypes,
//The columns that have numeric data types
MetaData::MODELS_DATA_TYPES_NUMERIC => $numericTypes,
//The identity column, use boolean false if the model doesn't have
//an identity column
MetaData::MODELS_IDENTITY_COLUMN => $identity,
//How every column must be bound/casted
MetaData::MODELS_DATA_TYPES_BIND => $dataTypesBind,
//Fields that must be ignored from INSERT SQL statements
MetaData::MODELS_AUTOMATIC_DEFAULT_INSERT => [],
//Fields that must be ignored from UPDATE SQL statements
MetaData::MODELS_AUTOMATIC_DEFAULT_UPDATE => []
];
}
/**
* Initializes the model's column map
*
* @param Phalcon\Mvc\ModelInterface $model
* @param Phalcon\DiInterface $di
* @return array
*/
public function getColumnMaps(ModelInterface $model, DiInterface $di)
{
$reflection = $di['annotations']->get($model);
$columnMap = [];
$reverseColumnMap = [];
$renamed = false;
foreach ($reflection->getPropertiesAnnotations() as $name => $collection) {
if ($collection->has('Column')) {
$arguments = $collection->get('Column')->getArguments();
/**
* Get the column's name
*/
if (isset($arguments['column'])) {
$columnName = $arguments['column'];
} else {
$columnName = $name;
}
$columnMap[$columnName] = $name;
$reverseColumnMap[$name] = $columnName;
if (!$renamed) {
if ($columnName != $name) {
$renamed = true;
}
}
}
}
if ($renamed) {
return [
MetaData::MODELS_COLUMN_MAP => $columnMap,
MetaData::MODELS_REVERSE_COLUMN_MAP => $reverseColumnMap
];
}
return null;
}
}
ModelUser.php
<?php
namespace Model;
use Phalcon\DI\FactoryDefault as DI;
use Fly\BaseModel as FlyModel;
/**
* User
*
* Represents a User
*
* @Source('fly_user');
*/
class User extends FlyModel
{
/**
* @Primary
* @Identity
* @Column(type="integer", nullable=false, column="u_id")
*/
public $id;
/**
* @Column(type="string", nullable=true, column="u_name")
*/
public $name;
/**
* @Column(type="string", nullable=true, column="u_email")
*/
public $email;
/**
* @Column(type="string", nullable=false, column="u_password")
*/
public $password;
/**
* @Column(type="integer", nullable=false, column="u_role")
*/
public $role;
/**
* @Column(type="string", nullable=true, column="u_avatar")
*/
public $avatar;
/**
* @Column(type="integer", nullable=false, column="u_status")
*/
public $status;
/**
* @Column(type="integer", nullable=true, column="u_datecreated")
*/
public $datecreated;
/**
* @Column(type="integer", nullable=true, column="u_datemodified")
*/
public $datemodified;
const STATUS_ENABLE = 1;
const STATUS_DISABLE = 3;
protected $lang;
public function initialize()
{
parent::initialize();
}
public function onConstruct()
{
$this->lang = DI::getDefault()->get('lang');
}
public function beforeCreate()
{
$this->datecreated = time();
}
public function beforeUpdate()
{
$this->datemodified = time();
}
public function validation()
{
$this->validate(new \Phalcon\Mvc\Model\Validator\PresenceOf(
[
'field' => 'name',
'message' => $this->lang->get('message_name_notempty')
]
));
$this->validate(new \Phalcon\Mvc\Model\Validator\Uniqueness(
[
'field' => 'email',
'message' => $this->lang->get('message_email_unique')
]
));
$this->validate(new \Phalcon\Mvc\Model\Validator\PresenceOf(
[
'field' => 'password',
'message' => $this->lang->get('message_password_notempty')
]
));
$this->validate(new \Phalcon\Mvc\Model\Validator\Numericality(
[
'field' => 'role',
'message' => $this->lang->get('message_role_isnum')
]
));
$this->validate(new \Phalcon\Mvc\Model\Validator\Numericality(
[
'field' => 'status',
'message' => $this->lang->get('message_status_isnum')
]
));
return $this->validationHasFailed() != true;
}
/**
* Extends Phalcon findFirst to handle cache
*/
public static function findFirst($parameters=null)
{
$data = parent::findFirst($parameters);
return $data;
}
}
UserController.php
public function addAction()
{
$redirectUrl = (string) urldecode(base64_decode($this->dispatcher->getParam('redirect')));
$formData = [];
$message = '';
if ($this->request->hasPost('fsubmit')) {
if ($this->security->checkToken()) {
$formData = array_merge($formData, $this->request->getPost());
$myUser = new \Model\User();
$myUser->assign([
'name' => $formData['fname'],
'email' => $formData['femail'],
'password' => $this->security->hash($formData['fpassword']),
'role' => $formData['frole'],
'avatar' => $formData['favatar'],
'status' => $formData['fstatus'],
]);
if ($myUser->create()) {
$this->flash->success($this->lang->get('message_add_success'));
} else {
foreach ($myUser->getMessages() as $msg) {
$message .= $msg->getMessage() . "</br>";
}
$this->flash->error($message);
}
} else {
$this->flash->error('CSRF Detected.');
}
}
$this->tag->prependTitle($this->lang->get('title_add'));
$this->breadcrumb->add($this->lang->get('title_add'), 'admin/user');
$this->breadcrumb->add($this->lang->get('title_adding'), 'admin/user/add');
$this->view->setVars([
'redirectUrl' => $redirectUrl,
'formData' => $formData,
'breadcrumb' => $this->breadcrumb->generate(),
'statusList' => \Model\User::getStatusList(),
'roleList' => \Model\User::getRoleList(),
]);
}