1

I have following code

$products = Product::get()->filter(array("OwnerID" => $this->ParentID))->sort("Name");  

And print_r($products->getIterator()) outputs following object

ArrayIterator Object
(
    [storage:ArrayIterator:private] => Array
        (
            [0] => Product Object
                (
                    [destroyed] => 
                    [model:protected] => DataModel Object
                        (
                            [customDataLists:protected] => Array
                                (
                                )

                        )

                    [record:protected] => Array
                        (
                            [ClassName] => Product
                            [LastEdited] => 2013-06-15 12:19:54
                            [Created] => 2013-05-07 03:55:23
                            [Code] => 2348934-SBC-AVL
                            [Number] => 2348934
                            [Name] => Sewing Machine
                            [Model] => SBC-AVL
                            [ID] => 259
                            [RecordClassName] => Product
                        )
                )
        )
)  

Problem

I want to modify product object of an object $products while iterating through it. I tried it with following code

<?php
 public function getProducts(){

    // First return all products by this parent id
    $products = Product::get()->filter(array("OwnerID" => $this->ParentID))->sort("Name");

    if(!$products->count()) return false;

    foreach($products->getIterator() as $product) {
        $product->product_name="{$product->Name}";
        $product->visible = true;
    }
    echo "<pre>".print_r($products,true)."</pre>"; exit;
    return $products;
}

I wanted to add property product_name and visible to the product object. But not working, if we print $products it outputs original output.

Question How to modify product object in the iteration of $products and add properties to it. I m expecting final output to be -

ArrayIterator Object
(
    [storage:ArrayIterator:private] => Array
        (
            [0] => Product Object
                (
                    [destroyed] => 
                    [model:protected] => DataModel Object
                        (
                            [customDataLists:protected] => Array
                                (
                                )

                        )

                    [record:protected] => Array
                        (
                            [ClassName] => Product
                            [LastEdited] => 2013-06-15 12:19:54
                            [Created] => 2013-05-07 03:55:23
                            [Code] => 2348934-SBC-AVL
                            [Number] => 2348934
                            [Name] => Sewing Machine
                            [Model] => SBC-AVL
                            [ID] => 259
                            [RecordClassName] => Product
                            [product_name] => Sewing Machine //<--- New property
                            [visible] => true  //<--- New property
                        )
                )
        )
)  
WatsMyName
  • 4,240
  • 5
  • 42
  • 73

4 Answers4

4

If $products->getIterator() is an array of product object then you can assign it in another variable and add new keys to it like,

$modifiedPro = $products->getIterator();
foreach($modifiedPro as $key=>$product) {
    $modifiedPro[$key]->product_name="{$product->Name}";
    $modifiedPro[$key]->visible = true;
}
echo "<pre>".print_r($modifiedPro,true)."</pre>"; exit;

Alternatively, you can use & before object to use $product as object by reference like,

foreach($products->getIterator() as &$product) {
    // -----------------------------^ here you need to use &
    $product->product_name="{$product->Name}";
    $product->visible = true;
}

Adding the & will keep your $products updated. To get it in deep refer PHP foreach loop and Language references

Updated, If you car using PHP version(<5.5) then it may causing error like Cannot create references to elements of a temporary array expression to resolve this type of error you need to make a temporary function

$a=products->getIterator();
foreach(z($a) as &$x) {
    $x->product_name="{$product->Name}";
    $x->visible = true;
}
print_r($a);

// indicate that this function returns by reference 
// and its argument must be a reference too
function &z(&$a)
{
    return $a;
}

I think Jack has already explained it well on Is there a rational explanation for this PHP call-by-value behavior? Or PHP bug?

Community
  • 1
  • 1
Rohan Kumar
  • 40,431
  • 11
  • 76
  • 106
  • This works well but in a method I have to return `$products`, which should contain modified `product` object. I have already tried your alternative solution which threw an error `Cannot create references to elements of a temporary array expression ` – WatsMyName Aug 22 '16 at 04:46
  • `$a=products->getIterator();` `foreach($a as &$x){}` works well, we don't need extra function `z`. But my problem is I don't need to return copy of an object, but I need to return original modified object, i.e. need to return `$products` – WatsMyName Aug 22 '16 at 05:17
1

Rohan Kumar explained how you can solve your problem in a generic way (using basic PHP methods). Since you're working with SilverStripe, here's a way to leverage the Framework to achieve what you want.

SilverStripes ViewableData objects have a customise method you can use to add additional fields to an object temporarily.

Here's how you would add your additional fields to all products:

public function getProducts()
{
    // First return all products by this parent id
    $products = Product::get()->filter("OwnerID", $this->ParentID)->sort("Name");

    if(!$products->count()) return null;

    $dataSet = [];
    foreach($products as $product) {
        $dataSet[] = $product->customise([
            'visible' => true,
            'product_name' => "{$product->Name}"
        ]);
    }

    return ArrayList::create($dataSet);
}

Not sure why you need to do that, though. I'd much rather use a DataExtension added to Product which adds the wanted fields? That way you can also access the information in other places (eg. the Product-Detail-Page) instead of just where you output your list.

bummzack
  • 5,805
  • 1
  • 26
  • 45
1

I solved this way, more like SilverStripe way. I accepted @RohanKumar's answer because his way works as well.

$newProduct = new ArrayList();
 foreach($products->getIterator() as $product) {
        $product->product_name="{$product->Name}";
        $product->visible = true;
        $newProduct->push($product);
    }

return $newProduct;
WatsMyName
  • 4,240
  • 5
  • 42
  • 73
0

Hi all I tried above all solutions but what worked for me in my code is as below

$updatedProducts = $response['products'];
foreach($updatedProducts as $key=>$prd){

if(in_array($prd->sku, $bgxArrU)){
    $updatedProducts[$key]['isBgx']= TRUE;
}else{
    $updatedProducts[$key]['isBgx'] = FALSE;
}
 }
$response['products'] = $updatedProducts;
Nilesh K
  • 124
  • 8