Is it possible to remove fields with *RECURSION* when using toArray() in Propel?
Asked Answered
I

4

7

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.

Impanation answered 29/6, 2016 at 13:28 Comment(0)
I
1

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.

Impanation answered 12/7, 2016 at 11:11 Comment(0)
K
3

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).

Kirstinkirstyn answered 5/7, 2016 at 13:16 Comment(2)
It appears there is a way to do it after all. See any problems with my solution to your knowledge?Impanation
I think you are correct, yes. Yours is the better answer :)Kirstinkirstyn
B
2

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

Botanist answered 11/7, 2016 at 15:50 Comment(1)
Thank you, although I don't need to extend the ArrayFormatter as it actually appears to work on its own, as I've shown in my own answer. I appreciate you setting me on the correct path!Impanation
I
1

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.

Impanation answered 12/7, 2016 at 11:11 Comment(0)
M
0

Did you try calling:

unset($return['Customer']['Orders']);
unset($return['Status']['Orders']);
Mauri answered 29/6, 2016 at 14:4 Comment(1)
Yeah, when I said about array walking I meant using unset - I'm specified that more explicitly now in my question. But I would still like something better, that I wouldn't have to hard-code to specific columnsImpanation

© 2022 - 2024 — McMap. All rights reserved.