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.
Related
I am using MongoDB with Laravel (php framework).
Using post method I am sending filter parameter to controller function.
ItemController.php
public function search_item(Request $request){
$item = $request->all();
$item = $this->item->search_item($item);
return response()->json($item,200);
}
Item.php (model file)
public function search_item($item){
$items = new item();
foreach($item as $key => $value) {
$items = $items::where($key, '=', $value)->get();
}
$items = json_decode(json_encode($pro));
return $items;
}
If I pass only one parameter then it's give me result perfectly but if I pass more then one parameter it's give me an error.
Non-static method Illuminate\Support\Collection::where() should not be called statically
Let me explain with example :
URL : http://localhost/use_good_store/public/search-item?brand_name=Bajaj
If I post above url it's give me perfect result but if I pass more then one parameter like
http://localhost/use_good_store/public/search-item?brand_name=Bajaj&model=Boxer it gives me above error.
There are a couple of things I would change in this code.
First, to address the error you're getting, you're accessing your search method incorrectly. It's an instance method, not a class (or static) method.
So instead of $this->item::search_items($item), it should be $this->item->search_items($item).
But I wouldn't recommend that. Since you're using the model anyway, go ahead and store a static search method on it for future use. It doesn't make much sense for it to be an instance method; you can't really call it on an instance because the point of it is to find many other instances.
Additionally, your query probably isn't going to work out the way you want since you're continually replacing the $items value in the for loop. if you're going to be using the QueryBuilder, you can just keep adding where() clauses to it all day long.
For this, I'd recommend adding a static searchItems() method that returns the results of a query which you would then convert to JSON in your controller, like so:
//Item.php model
public static function searchItems($itemSearchAttributes)
{
return static::where(function($query) use ($itemSearchAttributes){
foreach ($itemSearchAttributes as $key => $value){
$query->where($key, '=', $value);
}
})->get();
}
//ItemController.php handler method
public function search_item(Request $request){
$items = Item::searchItems($request->all());
return $response->json($items);
}
Worth noting; your search method here will exclude any records that do not match -all- of the provided key/values.
Try to make it like this :
$items = new item();
foreach($item as $key => $value){
$items = $items->where($key, '=', $value);
}
$items = $items->get();
I am using Propel 2. I am hydrating objects through the relations, like so:
$return = OrderQuery::create()
->joinWith('Customer')
->joinWith('Status')
->find()
->toArray(TableMap::TYPE_PHPNAME, true, [], true);
The resulting Array would look something like this:
{
"Id": 1,
"CustomerId": 1,
"StatusId": 1,
"Initiated": "2016-01-01T01:01:01+00:00",
"Customer": {
"Id": 1,
"Forname": "Test",
"Surname": "Smith",
"Orders": [
"*RECURSION*"
]
}
"Status": {
"Id": 1,
"Title": "title 1",
"Priority": 1,
"Orders": [
"*RECURSION*"
]
},
}
I want to remove the fields where the value is *RECURSION*. I tried using the $alreadyDumpedObjects (3rd) parameter to toArray() but that didn't seem to help. I could also do some form of array walking with unset() calls, but I'm hoping there's a better way, maybe with a formatter or something?
For bonus points, I'd quite like to remove the columns which define the foreign key relationship. For instance, CustomerId would go, but Customer would remain.
Note for brevity: This answer has some really helpful information, but there is a solution, despite this answer saying there isn't.
The RECURSION string is pretty much hardcoded in propel (in src/Propel/Generator/Builder/Orm/ObjectBuilder.php):
if (isset(\$alreadyDumpedObjects['$objectClassName'][\$this->hashCode()])) {
return '*RECURSION*';
}
I suppose you could override the object builder, but I doubt that's what you are looking for. Thus, the only other viable way is what you did not want to do, looping over the array and using unset. Something like this:
$array = StudyQuery::create()
->leftJoinWithInstitute()
->find()
->toArray();
var_dump($array);
echo PHP_EOL . PHP_EOL . PHP_EOL;
var_dump(cleanupData($array));
/**
* #param array $array
*
* #return array
*/
function cleanupData(array $array)
{
$relationsFound = [];
foreach ($array as $key => $item) {
if ($item === ["*RECURSION*"] || $item == "*RECURSION*") {
unset($array[$key]);
} elseif (is_array($item)) {
$array[$key] = cleanupData($item);
$relationsFound[] = $key;
}
}
foreach ($relationsFound as $relation) {
$key = $relation . 'Id';
if (isset($array[$key])) {
unset($array[$key]);
}
}
return $array;
}
This should also filter out the ***Id-fields, if that relation is present (provided the PHPName for the relation matches the column name).
When you call toArray method on a Propel ObjectCollection it will call the toArray on every item of the collection (the result of the query).
So there are two way to achieve what you're trying to do:
1) Overwrite the toArray method in the Customer and Status model classes or in the CustomerCollection and StatusCollection ones (a quick&dirty solution since it gets applied every time you use the toArray method...).
2) Extend the ArrayFormatter class to define a "skip Customer and Status strategy" in the format method and then add it as the formatter to use (with the setFormatter method on the collection) only when you need this particular behavior.
Some refs:
http://propelorm.org/documentation/reference/model-criteria.html#using-an-alternative-collection-class
http://propelorm.org/documentation/reference/model-criteria.html#using-an-alternative-formatter
The answer, it seems, appears to be very simple.
If I use a standard ArrayFormatter like this:
$return = OrderQuery::create()
->setFormatter('Propel\Runtime\Formatter\ArrayFormatter');
->joinWith('Customer')
->joinWith('Status')
->find()
->toArray();
What is returned is an ArrayCollection that, when toArray() is called on, is exactly the same as my original output apart from the recursion fields are missing.
Note, when getting one result, for instance with ->findPk(1), it will return an associative array, so you shouldn't use ->toArray() explicitly.
Did you try calling:
unset($return['Customer']['Orders']);
unset($return['Status']['Orders']);
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());
}
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;
I've been looking to a solution to my problem for a while without success so I'm asking here.
How can we return a json-encoded result on an array of objects (or just an object) containing private properties ?
Indeed, when you use json_encode($myObject), it won't display the private or protected properties, which are present everywhere in the model when using Symfony...
I'm surprised I couldn't find any method like json_encode that would call getters instead of properties themselves.
Any idea ?
EDIT
In that case I would rather do a unique function that looks like :
public function toArray() {
$vars = get_object_vars($this);
$result = array();
foreach ($vars as $key => $value) {
if (is_object($value)) {
$result[$key] = toArray($value);
} else {
$result[$key] = $value;
}
}
return $result;
}
in order to avoid rewriting every property name everytime...
But anyway I think I'll just create an array containing the vars I need, so that I won't touch the model (which is generated code).
Have you try GetSetMethodNormalizer ?
http://api.symfony.com/2.0/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.html
Ex. https://stackoverflow.com/a/6709828/520114
Right now there is no way for this. Only php serialize/unserialize handles the true serialisation of objects.
You'll have to implement them yourselve, or rather let objects return their json values themselves.
You will have to implement your own method toArray() where you expose all your private values in an array:
public function toArray()
{
return array(
'property1' => $this->myproperty1,
'property2' => $this->myproperty2
);
}
And call it like this:
json_encode(MyObject->toArray());
[Edit: this question is not about doctrine, but since you mention both symfony2 and the model, you can consider using Array Hydration for your model: http://www.doctrine-project.org/docs/orm/2.0/en/reference/dql-doctrine-query-language.html#array-hydration ]