Symfony/Doctrine: Entity "schema" annotation not environment dependent? - annotations

I am having trouble with getting functional tests to run in Symfony3 with Doctrine.
I have the code organized in two bundles with which need to be accessed by one EntityManager with entities stored in two different MySQL databases.
To achieve this, all Entities have a "schema" annotation in their definition, like this:
/**
* #ORM/Table(name="tablename", schema="schema")
* #Entity( ... )
*/
Without this setting, it has been my experience, that the Doctrine schema:create tool is not able to correctly create the entities in the right databases.
However it appears, that the schema annotation is not considered to be environment dependent.
So when I want to run functional tests that need to load fixtures, the ORMPurger tries to purge schema.tablename, where it should use the table/schema "test_schema".
Is there any way to keep the schema annotation but make it dependent on the environment, so that when the environment is "test", a different schema is used?
EDIT:
It appears that using the "schema" annotation for entities is pretty terrible all around when you are using different Symfony environments. At least when used in conjunction with MySQl, at least I think that that is the reason, since MySQL doesn't actually support schemas.
Every Symfony or Doctrine command I tried to take the schema annotation literally, regardless of the --env setting.

I've done some more digging and found what I needed to do perfectly laid out here:
Programmatically modify table's schema name in Doctrine2?
So I added an EventListener, that adds the correct schema according to the EM used so I don't need hard-coded schema annotations anymore.
Heres the code for the Listener I've made:
<?php
namespace /* ... */
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class MappingListener
{
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
/** #var EntityManagerInterface $entityManager */
$entityManager = $eventArgs->getEntityManager();
/** #var ClassMetadata $classMetadata */
$classMetadata = $eventArgs->getClassMetadata();
$database = $entityManager->getConnection()->getDatabase();
$classMetadata->table['schema'] = $database;
}
}

Related

Manage many to many relations in FosRest Bundle

I am using FosRest Bundle with symfony and I have a many to many relations between two tables.
For example:
class Site
{
/** ManyToMany **/
protected $languages;
}
class Language
{
/** ManyToMany **/
protected $sites;
}
I had records previously saved in each table separately but now I want to add relations between them in my new table site_language but I am not sure how to do it.
In SiteController I have these methods:
getSitesAction()
getSiteAction()
postSiteAction()
putSiteAction()
patchSiteAction()
deleteSiteAction()
Should I create a new method like postSiteLanguagesAction()?
Or modify the existing postSiteAction()?
Sort answer: probably both, kinda.
There are two separate concerns here:
altering the languages, that the site has ... though, I probably would just call it postLanguagesAction() instead
adding new sites
There are are actually two other methods, taht you forgot about: putSiteAction() and patchSiteAction() (TBH, I am not sure why you support both). When you update the existing site configuration, you will also need to have an ability to update its available language set.
P.S.
Regarding names, since you controller is most likely already called "Sites", I would have called the methods postResourceAction(), getResourceAction(), getCollectionAction(), etc., because it is kinda hard to see the difference between "posts" and "post" at a glance. And it would also make the "specialized methods" more noticeable.

Zend Model access in singleton class - best approach

I'm looking for best pattern/approach to access one table data in singleton class (in ZF 1.x). In details:
I have one singleton class (just like Zend_Date for example) that make for me some basic abstract stuff very detached from application reality.
In this class, in two points, I need to access to one db table and I need to make some basic operation on it.
It's not a problem to use my regular ZF models class inside functions of this singleton. It works fine. Now it look like:
class My_ZF_Singleton
{
...
public function someFunctionInMySingleton()
{
...
$oModel = new Model_My_Model_Form_ZF_Application();
$oModel->letsDoSomeStuffWithDb();
...
}
...
}
But I feel in my bones that it's not a very good solution, not so glamour as I would like to be. It make my singleton class more attached to application then it should be. I would like to use some other pattern to access this db data then application model class. I would be very thankfull for any clue or better solution - it's not a "hey I'm stuck probem" or "hey I've got an error" - I'm just looking for better solution.
Not sure I quite understand your question or want the point might be, but I'll try.
In ZF1 the database adapter is typically a singleton already. Multiple databases maybe connected to but each will require a unique identification. Typical access to the default adapter setup in the application.ini or Bootstrap.php:
$adapter = Zend_Db_Table::getDefaultAdapter();
a common way to provide access to a single database table and give access to the Zend_Db_Table api is to build a DbTable model:
class Application_Model_DbTable_TableName extends Zend_DbTable_Abstract
{
protected $_name = 'Table_Name' //required if classname does not match table name
protected $_primary = 'primary_key_column_name'//optional, use if primary key is not 'id'
}
You can treat this class as an instance of the default database adapter for a single table (works really well in a mapper). You can also add functions to this class to override or add to the default Zend_Db_Table api.
I hope this at least comes close.

Zend Framework: Models, Mappers; Default Fields in Mappers & Field Operations in Models?

I'm creating a simple ORM in Zend Framework, to roughly encapsulate a public library application, using the DbTable/Mapper/Model approach. I'm not sure if the way I'm doing my User-related classes is right, though, as I have some logic in Mapper_User, and some in Model_User.
Mapper_User
<?php
class Mapper_Users {
/*
createModelObject would be called by a Controller handling a Form_Regsiter's
data, to create a new Model_User object. This object'd then be saved by the
same Controller by calling Mapper_Users->save();
*/
public function createModelObject(array $fields) {
if(!isset($fields['date_registered']))
$fields['date_registered'] = date('Y-m-d H:i:s');
if(!isset($fields['max_concurrent_rentals']))
$fields['max_concurrent_rentals'] = 3;
return new Model_User($fields);
}
}
?>
In the method which creates new Model_User objects from scratch (as in, not pulling a record from the DB, but registering a new user), I instantiate a new Model_User with the name/username/password provided from a Form, then set a few object properties such as the registration date, "max books allowed at one time" and such. This data, being stuffed inside the Model_User by the Mapper_User, then gets written to the DB when Mapper_User->save(); gets called. The Mapper feels like the right place for this to go - keeps the Model light.
Is this right, or should default fields like this be set inside Model_User itself?
Model_User
<?php
class Model_User {
public function setPassword($value) {
$this->password = md5($value);
}
}
?>
When setting a user object's password, I'm doing this in Model_User->setPassword($value);, as you might expect, and doing $this->password = md5($value); inside this method. Again, this feels right - trying to do the md5 step in Mapper_User->save(); method would cause issues if the Model_User were one pulled from the DB, as the password field would clearly already be hashed.
And this is where my confusion's arising. To my mind, all the logic pertaining to "fields to do with a user" should either live in its Model, or its Mapper, but here I have some logic (default fields) in the Mapper, and some (field operations) in the Model. Is this right, or should I be trying to somehow get default fields in the Model, or field operations in the Mapper?
Thanks for taking the time to read this!
Edit for #RockyFord:
Mapper_User actually extends an Abstract I've written, as I don't like writing the same basic code in 500 Mapper_*.php files, so there's some bureaucracy due to that, but its effective __construct() is pretty simple:
<?php
class Mapper_Users {
public function __construct() {
$this->_db = new DbTable_Users();
if(!$this->_db instanceof Zend_Db_Table_Abstract)
throw new Exception('Invalid table data gateway provided');
}
}
?>
The DataMapper is responsible for populating the object with its data, as well as persisting it. It seems like you're mixing things when you call $user->save() because you're putting persistence logic within your domain object. This is a common approach when you're using the ActiveRecord pattern instead of DataMappers, which is a bad thing.
Your DataMapper should be responsible for saving the object $mapper->save($user); and it needs to update just the changed properties. So, the password will be updated only if you set the new hash.
UPDATE:
You said:
[...] trying to do the md5 step in Mapper_User->save(); method would cause
issues if the Model_User were one pulled from the DB, as the password
field would clearly already be hashed.
Creates a method called setPasswordHash() and use it when pulling from the database.
Remember: Don't look for things!
Instead of looking for the database inside your mappers, you should ask for it.
public __construct(Zend_Db_Table $dbTable) {
$this->dbTable = $dbTable;
}
It's all about Dependency Injection.
This may take awhile to answer completely but I'll start with the setPassword question.
your current:
public function setPassword($value) {
$this->password = md5($value);
}
Now this has nothing to do with convention or best practice but practicality.
ask yourself:
What happens when you retrieve a database record for your user object and that database record contains a hashed password?
Answer: When you construct the user object and call $this->setPassword($password); or equivalent, you will be applying the hash to a hash.
So you are almost obligated to hash the password in the mapper's save() method or the method used to update the password. Think of the hash value in the database table as the password and the value that's typed into the form field as a placeholder for that password.
Next Part:
To my mind, all the logic pertaining to "fields to do with a user" should either live in its Model, or its Mapper
This is mostly correct.
Everything that belongs to the object domain (Model_User) shall be addressed in the domain Model class (Model_User).
Mappers are only to translate (map) a data object (database row, json string, xml file, flat file, csv file ...) to a form that can instantiate a domain object (Model_User).
So you may end up with more then one mapper available for a given domain object or one mapper may map to more then one source of data.
It might help you if you stopped thinking of your data as "fields", which might tend to keep your head in the database, and instead think of your objects in terms of properties or characteristics.
Because when you get down to the most basic level a Model_User object is just:
class Model_User {
protected $id;
protected $name;
protected $password;
//continue....
}
all of the getters, setters, constructors and other methods are pretty much so we can put values into those variables.

Zend Framework Models and Doctrine 2 Entities

I've recently integrated Doctrine 2 into my ZF app, using the method introduced here:
http://www.zendcasts.com/unit-testing-doctrine-2-entities/2011/02/
I really like the way it works, however I'm confused a little bot on how this will affect the way I used to have my Models and Mappers.
Let me explain the confusion by an example,
Let's say we have User entities and Purchases as in the example given in ZendCast
Right now I have these entities that Doctrine uses
/library/ZC/Entity
User.php
Purchase.php
Before I used to have
application/models/
User.php (Application_Model_User)
Purchase.php (Application_Model_Purchase)
In classes in application/models/ I used to write functions to act on entities, (Fat model thin controller principle), for example if I wanted to send an email to a user, I would create a method named sendMail in Application_Model_User.
Now I'm not sure if I can add methods to files in /library/ZC/Entity, or if it's a good idea at all since Doctrine uses those files to manage database.
I rather have a separate model file, I also used to have mapper files which worked on more than one Model, for example if I wanted to email all inactive users I would create a method emailInactiveUsers to Application_Model_UserMapper.
How would I do that now?
I also googled a little bit and I found this:
http://net.tutsplus.com/tutorials/php/zend-framework-from-scratch-models-and-integrating-doctrine-orm/
It says
A scaffolding tool, called Doctrine_Cli that creates models from the database very quickly
However the command "generate-models-db" does not exist in my scripts/doctrine.php file. I'm not sure if this is something that Doctrine has stopped supporting in version 2 or what.
Adding methods and properties to your models which are not managed by Doctrine should be no problem. When it comes to mappers, you do not need them with Doctrine. Doctrine already takes care of mappings (e.g. via Annotations in your Entity-class) and for (complex) queries you have the EntityManager/Repositories.
I would place emailInactiveUsers() in a Service, which has access to the EntityManager, e.g.:
class UserMailService
{
private $em;
// Inject EntityManager, e.g. via setEntityManager() or __construct()
public function emailInactiveUsers()
{
$mail = new \Zend_Mail();
$users = $this->em->getRepository('User')->findBy(array('isActive' => false));
foreach ($users as $user) {
$mail->addTo($user->getEmail());
}
// And so on...
}
}
Something like sendMail() in my opinion belongs into a Service as it acts on a User-entity and requires a dependency to a Mailer, which should not be coupled with the model.
If a User does something it belongs in the model. If something acts on the User - in your case a Mailer, which takes the email-address from the user and sends out an email - it does not.

NetBeans autocompletion with Doctrine models?

I know it's possible to get IDE autocompletion from the *Table classes in Doctrine by doing things like this:
SomethingTable::getInstance()-><autocomplete>;
But the most important part is missing. I want autocomplete on the model classes themselves, not just the Table classes. It appears that Doctrine is not properly declaring the PHPdoc #return object types in the find and other standard model methods.
For example what I want to be able to do is this:
$something = SomethingTable::getInstance()->find($id);
$something-><autocomplete>
and have that pop up the methods and properties of the Something class.
I should mention too that I don't specifically care about using the SomethingTable::getInstance() syntax at all. ANY decent syntax that's standard Symfony is acceptable. Most of the time I'm fetching objects (or Doctrine_Collections) via custom queries like this:
$somethings = Doctrine_Query::create()
->from('Something s')
->leftJoin('s.SomethingElse s2')
->where(...);
By the way, in case it's not clear, I'm asking if there's any automatic solution to this with ANY of the various Doctrine find, fetch or query syntaxes. I'm NOT asking how to manually edit all the PHPdoc headers to cause the behavior I want.
I'm using NetBeans 6.9.1 and Symfony 1.4.12 with Doctrine, but not everyone working on the same code uses NetBeans.
The problem is that autogenerated *Table classes have the wrong phpdoc #return in the getInstance() method:
/**
* Returns an instance of this class.
*
* #return object MyModelTable
*/
public static function getInstance()
{
return Doctrine_Core::getTable('MyModel');
}
You just need to manually fix the #return line deleting the word "object":
* #return MyModelTable
And magically IDE autocompletion just works, giving you all the instance and static methods:
MyModelable::getInstance()->... //(you'll have autocompletion here)
I know, its a pain to have to manually fix this but at least it only have to be done once for each model *Table file.
In netbeans its quite easy:
$foo = ModelNameTable::getInstance()->find(1); /* #var $foo ModelName */
/* #var $foo ModelName */ tells netbeans to handle the variable $foo as a ModelName class.
just fix the generated model files by adding
/**
* #return ModelNameTable
*/
in the comment of the getInstance() method. This will provide autocomplete for the model file.
Regarding the find method, you can edit the comment of the class like this :
/**
* #method ModelName find()
*/
I think it might be possible for you to do this automatically by creating you own skeleton files.
Or not : Symfony Doctrine skeleton files
You could use sed to achieve this, or perhaps build your own task using the reflection api.