How to make doctrine ignore database column prefixes? - doctrine

Typically i create my entities in symfony2/doctrine from this console commands :
$php app/console doctrine:mapping:import TestSiteBundle yml
$php app/console doctrine:generate:entities Test --path=src/
but my table columns have prefixes like this :
table: user
id_user
id_address (FK)
nm_name
dt_created
bl_active
and it generates entities like this :
$idUser
$idAdress
$nmName
$dtCreated
$blActive
how can i ignore my column prefixes ? do i need to change my entire database column names ?

I think you can add the name like this:
Doctrine\Tests\ORM\Mapping\User:
fields:
created:
name: dt_created
type: datetime
you can see:
http://www.doctrine-project.org/docs/orm/2.1/en/reference/yaml-mapping.html
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php

Just so I understand, you want your database column name to be id_user and you want the entity property to be $user? If so, I don't think that's possible without doing some serious hacking of the core libraries. Basically you'd need to intercept the part that generates the entity properties and add your own rules on how to name them. You'd be much better off renaming your columns. IMHO, those prefixes are unnecessary. I would change id_user to user_id, nm_name to name, dt_created to created_at, and bl_active to is_active. Your column names and property names will not only match (this is a good thing) but they'll make more sense.
Hope this helps.

You can patch Doctrine to strip prefixes upon reverse engineering of your database.
Open this file in IDE: https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php
Add method to class DatabaseDriver implements Driver :
private function deprefixFieldName( $fieldName ) {
return implode('_',array_slice(explode('_',$fieldName),1));
}
Edit method:
public function setFieldNameForColumn($tableName, $columnName, $fieldName)
{
/* ADD */ $fieldName = $this->deprefixFieldName($fieldName);
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
}
Edit method:
private function getFieldNameForColumn($tableName, $columnName, $fk = false)
{
/* ... */
/* ADD */ $columnName = $this->deprefixFieldName($columnName);
return Inflector::camelize($columnName);
}
My method is so simple because my prefixes are all consistent ( i took the idea from Media Wiki ), yours may be more complex.
Here's the actual patch taken by Git from working system, just in case i made a typo in description: http://pastebin.com/FHeTCUjZ ( i wonder if patches in posts are allowed).

Related

Does eloquent pivot tables work for multi-word table names?

I have two multi-word models, let's call them FunkyModel and AnotherModel.
Will creating a pivot table named another_model_funky_model work?
The docs and examples I've come across all use single word model names like this: model A - User, model B - Address, and pivot table will then be address_user.
If you dive into the source code of the BelongsToMany relation function, you'll find that if you haven't provided a $table, the code will execute the function joiningTable. This uses the current model and the passed related class, snake cases the names and then puts them in alphabetical order of each other.
Simply said, no matter if you have a single word or a couple, the result will always be the 2 classes snaked, in alphabetical order. Note that the alphabetical order is applied by the default php sort.
Examples:
Department + Occupation > department_occupation
AwesomeModel + LessInterestingModel > awesome_model_less_interesting_model
Role + UserPermission > role_user_permission
You can even try and see what the auto-generated name is by simply calling the following:
(new Model)->joiningTable(OtherModel::class, (new OtherModel));
Yes it would work, you can also name it whatever you want, you just need to declare the table name in the relation (same goes for the foreign keys)
class FunkyModel
{
public function anotherModels()
{
return $this->belongsToMany(AnotherModel::class, 'pivot_table_name', 'funky_model_id', 'another_model_id');
}

Replace legacy column with a relation

I have a blog app on Yii2 with a really old DB
I replaced the text column post.author with a relation post.author_id.
For support reasons, the old column is active.
Now when I try to use $post->author->name i get the string column first, instead of the relation ...
Using the getter works fine $post->getAuthor()->name , but this will be hard to maintain.
Is there some standard solution for this, to ignore the post.author property,
and to favor the Author relation instead ?
You could rename the relation. If you rename the method getAuthor to getPostauthor the relation property will become postauthor (automatically) and you can fetch it via $post->postauthor->name
$post->getAuthor() returns ActiveQuery and you can't do $post->getAuthor()->name.
You may add getter to your model:
public function getAuthorName() {
$author = $post->getAuthor()->one();
return $author ? $author->name : null ;
}
Or, rename the relation.

Extbase Mapping with non-TYPO3-table

I have too classes and two non-TYPO3-tables. I defined a non-TYPO3-table as a table without uid, pid, etc. columns.
My two classes:
class Tx_Abc_Domain_Model_Location extends Tx_Extbase_DomainObject_AbstractEntity
class Tx_Abc_Domain_Model_Facility extends Tx_Extbase_DomainObject_AbstractEntity
My two tables (with columns):
locations
zipcode
city
facility_id
facilities
facility_id
name
I've mapped the attributes like this:
config.tx_extbase.persistence.classes {
Tx_Abc_Domain_Model_Location.mapping {
tableName = locations
columns {
zipcode.mapOnProperty = zipcode
city.mapOnProperty = city
facility_id.mapOnProperty = facility
}
}
Tx_Abc_Domain_Model_Facility.mapping {
tableName = facilities
columns {
facility_id.mapOnProperty = uid
name.mapOnProperty = name
}
}
}
My problem:
The facility attribute of my location model got the type Tx_Abc_Domain_Model_Facility and when I'm looking for a location via the LocationRepository it builds me a location model which contains a facility model.
The problem appears, when I the search I am doing returns several results. i.e. the location with the zipcode 12345 has two different facilities (and the table locations got two rows with different facility_ids), then I would expect to get two location models and each of it got the right facility model.
But instead I get the two location models, which have all same facility model inside. They've got all the facility of the first found location.
Even if I change the type of the facility attribute to integer, there are the wrong ids. But if I enable raw query result in repository I get the correct ids.
I get also the correct ids or models, when I add to both tables an uid-column.
Is there no possibility to map tables without uid column with Extbase models?
Thanks.
Okay, the answer to my last question is: Yes, there is no possibility to map tables without uid column with Extbase models.
There is an existing ticket on forge: http://forge.typo3.org/issues/25984
The reason seems to be the hardcoded $row['uid'] in mapSingleRow() method in Tx_Extbase_Persistence_Mapper_DataMapper class.
If it's not alot of tables you have to map, a work-around could be to create views for those tables to just map the uid.
I.e.:
CREATE VIEW tx_abc_domain_model_facility AS
SELECT facility_id AS uid, facilities.* FROM facilities;

ActiveRecord search returns 'Syntax error or access violation' error

In my Yii application, I have a model that represents siteconfig table and have four columns:
integer config_id,
string key,
string value,
string update_time.
I created a model using Gii (to ensure that I will not make any mistakes). I don't publish entire code here, cause this is 100% unmodified by me, standard model code generated by Gii. Since my problem is related to search, I only publish important part of generated code (the search() method):
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('config_id',$this->config_id);
$criteria->compare('key',$this->key,true);
$criteria->compare('value',$this->value,true);
$criteria->compare('update_time',$this->update_time,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
I'm trying to use generated model in normal Yii ActiveRecord search like that:
$etona = new SiteConfigurationRecord();
$crit = new CDbCriteria();
$crit->select = "value";
$crit->condition = "key=:key";
$crit->params = array(":key"=>"sitename");
$etona = $etona->find($crit);
But, instead of getting expected search results, a strange (for me) error occurs:
CDbCommand failed to execute the SQL statement: SQLSTATE[42000]:
Syntax error or access violation: 1064 You have an error in your SQL
syntax; check the manual that corresponds to your MySQL server version
for the right syntax to use near 'key='sitename' LIMIT 1' at line 1.
The SQL statement executed was: SELECT value FROM siteconfig t
WHERE key=:key LIMIT 1
Where did I go wrong?
You used key for column name, which is a reserved word in MySQL. Yii uses table alias in queries, but does not take any special care in case of reserverd word used as columns names. So, you have to take care of this by yourself.
For example:
$etona = new SiteConfigurationRecord();
$crit = new CDbCriteria();
$crit->select = "value";
$crit->condition = "t.key=:key"; // 't' is default alias
$crit->params = array(":key"=>"sitename");
$etona = $etona->find($crit);
This should solve your problem.
As #Dmitry explained, SQL doesn't allow you to use the column name key. The Yii call in the code in your answer works because Yii performs parameter binding automatically, using names other than reserved words for the parameters. And it also uses fully-qualified column names (prefixes all column name references with <tablename>., regardless of what invalid column name (reserved words) you pass the findByAttributes method.
now it works.. ^^
i just use this code...
$etona = SiteConfigurationRecord::model()->findByAttributes(array('key'=>'sitename'));
maybe i need to study activerecord more somehow...
but still i don't know why the code above doesn't work

How do I setup super- and sub-type relationships in CodeIgnitor's DataMapper ORM?

I'm running an online food journal where users can record journal entries. There are four types of entries: food entries, exercise entries, measurements, and completed tasks. Entries have several attributes in common (e.g., id, amount, unit_id, etc), but they also have type-specific attributes (e.g., food_id, exercise_id, etc). That's a perfect candidate for a super- and sub-type relationship.
Here are my tables (simplified):
CREATE TABLE entries
`id` int
`user_id` int
`created` datetime
`entered` datetime
`amount` float
`unit_id` int
PRIMARY KEY id
CREATE TABLE exercise_entries
`entry_id` int
`exercise_id` int
PRIMARY KEY entry_id
CREATE TABLE food_entries
`entry_id` int
`food_id` int
PRIMARY KEY entry_id
So my question is, how do I setup super- and sub-type relationships using CodeIgniter's DataMapper ORM? I've looked at the User Guide's Relationships and Advanced Relationships sections, but I can't find anything.
If it's not possible with DataMapper, I can think of a few solutions:
Roll sub-type attributes up (1 big table, ugh)
Roll super-type attributes down (4 separate tables, ugh)
Go nuclear and use Doctrine 2.0 ORM (YAML config files, ugh!)
Use a different framework whose native ORM supports table inheritance (I shortlisted Kohana and FuelPHP with CodeIgniter).
Manually code super- and sub-type relationships (defeats purpose of ORM in the first place).
I'm not thrilled with any of my options. Option 1 and 2 create their own headaches (see the bottom of this article). Option 3 seems like surgery with a sledgehammer. I'm open to Option 4 because I haven't started writing any framework code (it was a really tough choice between CI and Kohana). Option 5 is where I am now.
Any suggestions? Thanks for the help!
I haven't tried this with DataMapper, but you might try (making sure to call the parent constructor and all that). I would assume that Exerciseentry would inherit all of the properties/methods from Entry - but not sure if DataMapper would handle it this way:
class Entry extends DataMapper {
}
// you may have to explicitly include Entry.php in this file:
class Exerciseentry extends Entry {
}
If that doesn't work, you can basically create two objects that are related ( not really pure OOP principle, but would get the job done ):
class Entry extends DataMapper {
// ... some stuff
var $has_many = array('exerciseentry', 'foodentry');
// ... some more stuff
}
class Exerciseentry extends DataMapper {
// ... some stuff
var $has_one = array('entry');
// ... some more stuff
}
class Foodentry extends DataMapper {
// ... some stuff
var $has_one = array('entry');
// ... some more stuff
}
// then when you get an entry, you'd do this
$my_exercise_entry = new Exerciseentry(1);
$my_exercise_entry->include_related('entry', array('user_id', 'amount', 'unit_id');
$my_exercise_entry->get();
echo 'Amount is: ' . $my_exercise_entry->entry_amount;
// etc

Resources