Proper way to get an array of values from Doctrine2 - php

I'm currently coding a newsletter system. In order to send the mail, I need to get all e-mail addresses from my database (of course).
So I created a custom repository method, as follows :
public function getEmailAddresses()
{
$query = $this->getEntityManager()->createQueryBuilder()
->select('u.email')
->from('AppBundle:User', 'u')
->where('u.isNewsletterSubscriber = true')
;
$results = $query->getQuery()->getResult();
$addresses = [];
foreach($results as $line) {
$addresses[] = $line['email'];
}
return $addresses;
}
I am wondering if there is a better way to do so than treating the result to get a "plain" array containing only e-mail addresses. In effect, after $query->getQuery()->getResult(), I get something like this :
'results' =>
[0] => array('email' => 'first#email.com')
[1] => array('email' => 'second#email.com')
And as I said, I want something like this :
array('first#email.com', 'second#email.com')
Does a cleaner way to do that exist using Doctrine2 built-in methods ? I've tried with different hydratation modes but nothing worked.
Thanks in advance :)

You could probably create a custom hydrator, but there's really no issue with just doing it the way you are right now. You could also do it in the following ways:
PHP <= 5.4
return array_map('current', $addresses);
PHP >= 5.5
return array_column($addresses, 'email');
The array_column function was introduced in PHP 5.5.0 and does what you're looking for. The array_map function will work otherwise, calling PHP's internal current function which simply returns the value of the current element (which is always initialized to the first element of that array).
Be careful with using array_map if you have a large number of rows returned, because it will likely be slower and it will definitely take up a lot more memory since it has to copy the array.

You can run pure sql with doctrine (DBAL):
example:
public function getEmails()
{
$connection = $this->getEntityManager()->getConnection()->prepare('SELECT u.email FROM user AS u');
$connection->execute();
return $connection->fetchAll(\PDO::FETCH_COLUMN);
}

Try other $hydrationModes, maybe that help
getResult( mixed $hydrationMode = Doctrine\ORM\AbstractQuery::HYDRATE_OBJECT )

I would rather use the getArrayResult method, so doctrine must not hydrate each object (this is the expensive task from doctrine).
public function getEmailAddresses()
{
$q = $this->getEntityManager()->createQuery('SELECT u.email FROM AppBundle:User u WHERE u.isNewsletterSubscriber = true');
return array_map('current', $q->getArrayResult());
}

Related

Create a find method with PDO?

In the past I've worked with framework as Slim or CodeIgniter, both provide method such as getWhere(), this method return true or false if the array content passed to the getWhere was found on database table.
Actually I've created my own layer class that extends PDO functionality, my goal is create a method that take care to look for a specific database content based on the supplied parameters, currently I created this:
public function findRecordById($table, $where = null, $param = null)
{
$results = $this->select("SELECT * FROM $table WHERE $where",$param);
if(count($results) == 0)
{
return false;
}
return true;
}
for search a content I simply do:
if(!$this->db->findRecordById("table_name", "code = :key AND param2 = :code",
array("key" => $arr['key'], "code" => $arr['code']))){
echo "content not found";
}
now all working pretty well but I think that the call on the condition is a bit 'too long and impractical, I would like to optimize everything maybe going all the content into an array or something, but until now I have a precise idea. Some help?
I don't quite understand your question, but for the code provided I could tell that such a method should never belong to a DB wrapper, but to a CRUD class, which is a completely different story.
If you want to use such a method, it should be a method of a Model class, and used like this
$article = Article::find($id);
While for a database wrapper I would strongly suggest you to keep with raw SQL
$sql = "SELECT * FROM table_name WHERE code = :key AND param2 = :code";
$data = $this->db->query($sql, $arr)->fetchAll();
is the clean, tidy, and readable code which anyone will be able to read and understand.
As a bonus, you will be able to order the query results using ODER BY operator.

Conditionally building an Eloquent query

The Context
I'm using Laravel's Eloquent as my ORM. I am creating an API endpoint which provides access to Cars which have several attributes (color, make, status).
My endpoint allows clients to filter the return value by any subset of those attributes, if they provide no attributes then I will return everything.
The Question
I want to build a conditional query, which starts from "all" and narrows down based on which parameters have been specified. Here's what I've written:
public function getCars(Request $request)
{
$results = Cars::all();
if($request->has('color'))
$results = $results->where('color', $request->input('color'));
if($request->has('make'))
$results = $results->where('make', $request->input('make'));
if($request->has('status'))
$results = $results->where('status', $request->input('status'));
return $results->toJson();
}
If I call this with no parameters the API returns a list of all cars in the database.
If, however, I specify (for instance) status of 0 the API returns an empty set, despite the fact that some cars have status of 0.
Am I approaching this incorrectly? Is there something fundamental I'm missing?
Note that if instead I write:
$results = Cars::where('status', 0);
return $results->get();
The list of cars is properly generated
You should change your function like this:
public function getCars(Request $request)
{
$results = Cars::query();
if($request->has('color'))
$results = $results->where('color', $request->input('color'));
if($request->has('make'))
$results = $results->where('make', $request->input('make'));
if($request->has('status'))
$results = $results->where('status', $request->input('status'));
return $results->get()->toJson();
}
You could try this, for simplicity.
$query = Cars::query(); // no query executed, just give us a builder
$query->where(array_only($request->all(), ['color', 'make', 'status'])); // where can take a key value array to use
// update: only taking the vars you need, never trust incoming data
return $query->get(); // will be converted to Json for you
This only queries the DB for what you need. Yours is returning all results then filtering through them in a collection.
Update:
As Joseph stated, there is different functionality between $request->only() and array_only. The functionality of array_only is wanted here.

Multi row toJSON function using Propel and Backbone

I'm trying to make what should be a very simple "list all" function using Propel ORM - for Backbone.js to read. This is what I want to do, and in my opinion, should work:
$users = UsersQuery::create()
->find();
echo $users->toJSON();
However, when I'm running that, the results I'm getting are:
{"Users_0":{"Id":1,"EmailAddress":"sdf","Password":"sdf","CreatedAt":null,"ModifiedAt":null},
"Users_1":{"Id":2,"EmailAddress":"dsf","Password":"sdf","CreatedAt":null,"ModifiedAt":null}}
Whilst it's valid JSON, the fact that ever row is an array in the main array is throwing off my JSON. What I need it to return is JSON like this:
[{"Id":1,"EmailAddress":"sdf","Password":"sdf","CreatedAt":null,"ModifiedAt":null},{"Id":2,"EmailAddress":"dsf","Password":"sdf","CreatedAt":null,"ModifiedAt":null}]
I've created the below function (as a test) and it works perfectly, but surely Propel (or Slim, the framework I'm using) has way of stopping everything being inside an array? Here the hack;
$users = UsersQuery::create()
->find();
$json = '[';
foreach($users as $user){
$json = $json.$user->exportTo('JSON').',';
}
$json = $json.']';
echo str_replace("},]", "}]", $json);
Any help would be greatly appreciated! Thanks all.
I hate to say it, but I think this is just one of those "that's how Propel works" situations. That said, you could improve your helper function a little to be more robust.
I would put this code in your UserQuery class:
class UsersQuery extends BaseUsersQuery {
...
public function toJSONArray() {
$users = $this->find();
$userArray = array();
foreach($users as $user){
array_push($userArray, $user->toArray());
}
return json_encode($userArray);
}
}
And then use it like so...
$userJSON = UsersQuery::create()->toJSONArray();
Or if you have other criteria...
$userJSON = UsersQuery::create()
->filterBySomeField("someValue")
// other Criteria ...
->toJSONArray();
is there a possible solution to use this and a select filter in one statement. Something like this:
$ojson = TblproductQuery::create()
->select(array('ProdtID', 'DivnID'))
->toJsonArray();
One thing the accepted answer does not address is when you have an object with a nested collection. Like maybe you have a Bunch of tests with answers like so:
[
{
id:test1,
answers : [
{ id: 1, answer: pig},
{ id: 2, answer: dog}
]
},
{
id:test2,
answers : [
{ id: 5, answer: duck},
{ id: 6, answer: swan}
]
}
]
The above won't play nicely with backbone collections when you try to use the accepted answer. This is because every propel model will call the PropelCollection::toArray() method on any propel collections within itself
The PopelCollection::toArray() method will only return itself as an associative array in php which gets converted to an unsorted set in JSON rather than an array. Backbone collections are sorted (arrays) only.
To fix this, I just changed the toArray() method in the propel source file PropelCollection.phpto the following:
public function toArray(
$keyColumn = null,
$usePrefix = false,
$keyType = BasePeer::TYPE_PHPNAME,
$includeLazyLoadColumns = true,
$alreadyDumpedObjects = array()){
$ret = array();
foreach ($this as $key => $obj) {
array_push($ret, $obj->toArray($keyType, $includeLazyLoadColumns,
$alreadyDumpedObjects, true);
}
return $ret;
}
I haven't seen how this affects the toXML or toYAML methods, but it allows the toJSON method to work as I want with nested collections like my example above.

How to access Object Element inside an Array without a foreach loop

I'm trying to learn the use of the Zend Framework and I am facing now the following issue.
I am reading some information from the database for a specific Post. I use Datamapper and Models.
$postMapper = new Application_Model_PostMapper();
$post = new Application_Model_Post();
$details = $postMapper->find($postID, $post);
$this->view->postDetail = $details;
In my View, I use a foreach($this->postDetail as $value) to read all the Post Information. But I was wondering now, if I can also access an Information without the foreach. I need just the Email Adress in the Controller and can't see why I would need a foreach. But how would I access this? A Zend_Debug comes with the following results:
array(1) {
[0] => object(Application_Model_Post)#87 (27) {
["_email":protected] => string(10) "test#testmail.com"
It does sound like a very stupid question, but I just don't find a way to read out the Email Adress inside the Controller. Can someone give me a hint?
In your Application_Model_Post class, you would create an accessor method in order to get the private value.
So create a method like this.
public function getEmail(){
return $this->_email;
}
To be honest, I think you will already have these methods if you are using a datamapper correctly.
When you get data from Model/db by fetchAll, eg:
$result = $this->fetchAll($select);
you can
$result->toArray();
//access like array
$result[0]->some_col_1;
$result[0]->some_col_2;
$result[1]->some_col_1;
$result[1]->some_col_2;
...
one of these should work:
if $details returns an array: $email = $details['email']; or =$details[0]['email'];
if $details returns an object: $email = $details->email;

PHP: Passing functions to a class

I made a class. I give it some objects (mostly retreived database rows) as input, and tell it which fields it has to show, and which buttons I want. Then it renders a very nice html table! It's pretty awesome, I think.
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->buttons = array('edit','delete');
$ot->render();
However, I also want to be able to manipulate the data it shows. For example, i want to be able to truncate the 'description' field. Or to display an image thumbnail (instead of 'picture.jpg' as text). I don't know how to pass these functions to the class. Perhaps something like:
$ot->functions = array(null,null,'truncate','thumbnail');
But then I don't know how to convert these strings to run some actual code, or how to pass parameters.
There must be a nice way to do what I want. How?
Check this question and the answer is:
As of PHP5.3 you could use closures
or functors to pass methods
around. Prior to that, you could write
an anonymous function with
create_function(), but that is
rather awkward.
But what you are trying to achieve is best solved by passing Filter Objects to your renderer though. All filters should use the same method, so you can use it in a Strategy Pattern way, e.g. write an interface first:
interface Filter
{
public function filter($value);
}
Then write your filters implementing the interface
class TruncateFilter implements Filter
{
protected $_maxLength;
public function __construct($maxLength = 50)
{
$this->_maxLength = (int) $maxLength;
}
public function filter($value)
{
return substr(0, $this->_maxLength, $value) . '…';
}
}
Give your ObjectTable a method to accept filters
public function addFilter($field, Filter $filter)
{
if(in_array($field, $this->fields)) {
$this->_filters[$field][] = $filter;
}
return $this;
}
And when you do your ObjectTable instantiation, use
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->addFilter('description', new TruncateFilter)
->addFilter('name', new TruncateFilter(10))
->addFilter('image', new ThumbnailFilter);
Then modify your render() method to check if there is any Filters set for the fields you are rendering and call the filter() method on them.
public function render()
{
foreach($this->fields as $field) {
$fieldValue = // get field value somehow
if(isset($this->filters[$field])) {
foreach($this->filters[$field] as $filter) {
$fieldValue = $filter->filter($fieldValue)
}
}
// render filtered value
}
}
This way you can add infinite filters.
PHP has a pseudo-type called "callback", which is actually an ugly closure in disguise. You can call such callbacks using call_user_func() or call_user_func_array():
$callback = 'strlen';
echo call_user_func($callback, '123');
// will output 3 (unless you're using a strange encoding)
You are looking for create_function().
However, creating functions on runtime and adding them to a class doesn't sound right to me. It's likely to become a maintenance nightmare very quickly. There are better ways to achieve what you want. What kind of manipulation would the functions to to the data? How about storing them in a "tools" class and connecting that with the table object when needed?
$functions = array(null,null,'truncate','thumbnail');
$function_1 = $functions[3];
$my_string = 'string to truncate';
$result = call_user_func($functions[2], $my_string);
If you want to pass multiple parameters, use call_user_func_array instead.
You might also want to explore call_user_func, which allows you to call a function based on a string representing its name.

Categories