Symfony2 Doctrine2 how to add object if not fetched from db - php

Check this example: http://symfony.com/doc/current/book/doctrine.html#fetching-objects-from-the-database
What I got is:
$results = $applicationsRepo->findByInName($appInNames);
Where appInNames is an array looking like this:
array(
app1_name => app1_name,
app2_name => app2_name,
app3_name => app3_name,
...
)
I want to create an entity object when it's not found. How to check if app1_name was returned and if not create one ?

If you need to get an array of all entities that have inName attribute set to either of app1_name, app2_name, etc with a default value if it does not exists, you can use findOneBy instead of findBy and create your entity if the result is NULL.
May not be the most efficient method because of the loop but it give you what you need :
$results = array();
foreach($appInNames as $appInName) {
$app = $applicationsRepo->findOneByInName($appInName);
if(!isset($app)) {
// Create you entity here
}
$results[$appInName] = $app;
}
A more efficient method may be to write a custom Repository and use the queryBuilder to add all your OR conditions. You'll get all the existing entities in one query, you will then have to parse the result to create the missing entities.

Related

Can we add custom values to a CakePHP Table Object?

I have a Cake Object when querying a table:
$invoices = TableRegistry::get('invoices')->find('all', ['conditions' => ['order_number =' => $orderNumber]]);
This works fine. I then want to add other array key/values to this Object, like this one:
$invoicesTmp = array();
$invoicesTmp['customer'] = "name of customer";
But $invoicesTmp is incompatible with $invoices. (one is an array, other is an CakePHP Object)
I have tried this:
compact($invoices, $invoicesTmp);
but that didn't worked.
The find() method of a Table object returns a Cake\ORM\Query object. This object is used to build SQL queries and to execute them. It has some features to define how the results from the query should be returned.
When CakePHP fetches results from the database the records are stored as an array, and CakePHP then converts them to Entity objects. A process called "hydration" of entities. If you disable hydration the records are returned as just an array.
$query = TableRegistry::get('invoices')
->find()
->where(['order_number'=>$orderNumber])
->enableHydration(false);
foreach($query as $record) {
pr($record);
}
The above creates a query object, and you can iterate over the query records because the object itself supports iteration.
The query object implements the Cake\Collection\CollectionInterface interface, which means we can perform a bunch of collection methods on it. The most common method is the toArray().
$invoices = TableRegistry::get('invoices')
->find()
->where(['order_number'=>$orderNumber])
->enableHydration(false)
->toArray();
The $invoices variable is now a valid array object holding the all the records with each record as an array object.
You can now easily use array_merge to assign extra metadata to each record.
$invoices = array_map(function($invoice) {
return array_merge(['customer'=>'name of customer'], $invoice);
}, $invoices);
$this-set(compact('invoices'));
Updated:
Based upon the comments it appears you wish to use two different tables with different column names, but those columns represent the same data.
Field Aliases
You can rename fields in the SQL query to share a common alias.
$table = TableRegistry::get($whichTable ? 'table_a' : 'table_b');
$records = $table->find()
->select([
'id',
'invoice_id',
'name' => ? $whichTable ? 'customer_name' : 'invoice_name'
])->all();
The above selects a different column for name depending upon which table is being used. This allows you to always use $record->name in your view no matter which table.
I don't like this approach, because it makes the source code of the view file appear to reference a property of the entity that doesn't really exist. You might get confused when returning to the code later.
Field Mapping
From a MVC perspective. Only the controller knows what a view needs. So it's easier if you express this knowledge as a mapping.
$map = [
'id'=>'id',
'invoice_id'=>'invoice_id',
'name' => ? $whichTable ? 'customer_name' : 'invoice_name'
];
$table = TableRegistry::get($whichTable ? 'table_a' : 'table_b');
$records = $table->find()
->select(array_values($map))
->all();
$this->set(compact('records','map'));
Later in your view to output the columns you do it like this:
foreach($records as $record) {
echo $record->get($map['name']);
}
It becomes verbose as to what is happening, and why. You can see in the view that the controller provided a mapping between something called name and the actual field. You also know that the $map variable was injected by the controller. You now know where to go to change it.

retrieve a field from database table using symfony doctrine

I am trying to create an api to retrieve a field from the database using symfony2 doctrine but the query keeps returning a result not found. This is my attempt
/**
* #Route("/get/{email}")
*/
public function emailAction($email)
{
$singleresult = $this->getDoctrine()->getRepository('Api3Bundle:Influ')->find($email);
if ($singleresult === null) {
return new View("user not found", Response::HTTP_NOT_FOUND);
}
Please how can I modify the above url so that on calling the uri endpoint let it return the email in the argument.
If in you entity has a field named "Email", you could use findOneByEmail(), it is a dynamic method names to find a single object based on a column value:
->getRepository('Api3Bundle:Influ')findOneByEmail($email);
You can also use findOneBy() method to easily fetch objects based on multiple conditions,for example you want to retrieve an email but just if it is active, in that case you could pass and array:
->getRepository('Api3Bundle:Influ')findOneBy(
array('email' => $email, 'active' => 1)
);

ZF2 Doctrine Hydratation

When I query a table, for example:
$query = $this->entityManager->createQueryBuilder();
$query->select('TaPost')->from("Application\Entity\TaPost", 'TaPost');
return $query->getQuery()->getResult()
)
I get an array of object "Tapost".
Is there an easy way (and not ruining performance) to get an array of a given new class ? An equivalent to zend/db/sql:
new HydratingResultSet(new \Zend\Stdlib\Hydrator\ClassMethods(), new myNewClass())
Do you want to get directly array result? There are two way. You get an entity object which is \Application\Entity\TaPost. You can create a method to your entity like that
class TaPost {
// Your entity attributes and methods
// ...
public function toArray()
{
return array(
"id" => $this->getId(),
"title" => $this->getTitle(),
"description" => $this->getDescription(),
// ...
);
}
}
And use them them when your for loop.
Another solution is, you can use Doctrine HYDRATE_ARRAY
$results = $query->getQuery()->getResult( Doctrine\ORM\Query::HYDRATE_ARRAY );
Try to use doctrine hydrator instead of zend hydrator.
$model = new \Blog\Model\Post();
$hydrator = new \DoctrineModule\Stdlib\Hydrator\DoctrineObject($this->getEntityManager(), 'Blog\Model\Post');
$model = $hydrator->hydrate($array, $model);
thank you for your answer but that's not exactly my objective.
I'm trying to do the tutorial and, instead of zend-db-sql i'm using Doctrine.
I have a method findAll() which have to return an array of objects from class PostInterface based on a custom model (post).
With Doctrine, I get an array of TaPost (TaPost being an entity of Doctrine) but I need to return an array of Post.
How can I tell Doctrine to automatically hydrate Post and not TaPost ? will i need to made a foreach on my doctrine result and hydrate an object Post one by one ?
ps: with zned-sql, they do it when getting the result:
$resultSet = new HydratingResultSet(new \Zend\Stdlib\Hydrator\ClassMethods(), new \Blog\Model\Post());
return $resultSet->initialize($result);

Custom finder that returns a list when I do a toArray in cakephp 3

I know how to write a custom finder in CakePHP 3.x by looking at this.
I also know how to get a "list" by using find('list') and then use toArray via this
What I want to do is to write a custom finder that will ensure that I can use toArray on the result of the custom finder and get back a similar array if I use find('list').
My custom finder method name is findListForDynamicViews.
Hence I want
$query = $circlesTable->find('listForDynamicViews');
$data = $query->toArray(); // this gives me an associative array similar to the one we get from find('list')
How do I write the custom finder given that I know how to use where() for the query?
This is possible using result formatters, which is what the list finder does too:
https://github.com/cakephp/cakephp/blob/a64a62126287abbc3d8c53f48a5281ac77e791b0/src/ORM/Table.php#L897
Stripped to the bare essentials, you simply combine by key and value fields, which are usually the primary key and the display field:
$query->formatResults(function(\Cake\Datasource\ResultSetInterface $results) {
return $results->combine('key_field', 'value_field');
});
Maybe this short code will work in your case:
$query = $circlesTable->find('list')->find('listForDynamicViews');
Yo do not need to modify your custom finder at all.
I managed to do this with a custom finder like this:
// contained in my PropertiesTable.php
public function findCityList(Query $query) {
return $query->find('list', ['keyField' => 'id', 'valueField' =>'city'])
->select(['city'])->distinct(['city']);
}
The call to list on find() already returns an array so you wouldnt need to call toArray() after it.
// the retuning array
$citys = [
'7' => 'Olímpia',
'10' => 'São José do Rio Preto',
'17' => 'Guarujá'
];
Hope it helps.

DBTable creates new row instead off updating the existing

I try to update an DB entry with now data, but i am just creating an new entry:
$client =$this->clientTable->find($id);
$client->CompanyName = $request->getPost('CompanyName');
$this->clientTable->update();
$this->_redirect('client/index');
Zend_Db_Table_Abstract::find() method returns Zend_Db_Table_Rowset object. You should use method which will return you Zend_Db_Table_Row object and use it.
For example:
$clientRow = $this->clientTable->fetchRow(array('id' => $id));
$clientRow->CompanyName = $request->getPost('CompanyName');
$clientRow->save();
If your's table primary key name is not 'id', change it to suitable value in the first line of code above.

Categories