Jump to content
Larry Ullman's Book Forums

Dealing With Arrays Of Objects And Passing Variables Back To View


Jonathon
 Share

Recommended Posts

This I suppose isn't strictly for the Yii book. But I'm doing it through Yii.

 

I did build all my queries in Query Builder. Which has been fine I must admit, But I had a couple of glitches. For instance, I had been using a simple foreach($model as $row) to access through my records. The problem came when I wanted to give the <h1> on the page a better description. As far as I could see the only way was to access the $model was through a foreach loop. Which worked, but then when I came to echo out the majority of the data I got a "Couldn't rewind" error. Because i'd tried to use $model in another foreach loop - Do tell me if there is an easy fix to this though.

 

So, to cut a long story short, I thought I'd move back to AR and try to get these queries working. It's basically returning an array of objects. Which took me a while to get. I just wanted to check on something. My route, just uses the $id from the the URL, that's passed to the model and the data is returned as a $model to the controller which passes it on to my view. My question is regarding the passing of variables back to the view. 

 

Is it ok just to do this:

 

 
    public function actionIndex($id)
    {
        
    $items = Item::model()->returnItemsInArea($id);
    $recordCounter = (int)$id -1;
    
        $this->render('index', array(
        'model'=>$items,
        'id'=>$recordCounter
        ));
    }
 

 

Is that ok? I just pass back the $id variable (-1 to fall inline with the array indexes) to help select the correct model. So in my view

 

<h1><?php echo CHtml::encode($model[$id]['Area']); ?> Items</h1> // Displays the correct area

 

I then use it in a similar fashion to check that the $model isn't empty and then if not it returns a list of items. It seems to work correctly. But I wanted to check. I know Edward mentioned to me that it was a pain to access.

 

 
if(!empty($model[$id]['areaItems'])){
    
    foreach($model[$id]['areaItems'] as $row){
 
        echo '<h4>' . $row['title'] . '</h4>';
        // More data to output
    }
} else {
    echo 'No Items';
}
 
Link to comment
Share on other sites

I know you want Larry to answer you so i won't bother replying to your other questions but i found a cool way to access the array of objects so now it is not longer a pain.

 

$modelInbox = $this->model()->findAll($criteria);

if($modelInbox===null)
		{
			throw new CHttpException(404,'The requested page does not exist.');		
		} 
		else 
		{
			foreach($modelInbox AS $message) 
			{
    			echo $message->column1;
    			echo $message->column2;
			}
		}

The other way you can get the elements out of the objects array i tried was ugly, i used print_r function to obtain information to get this:

 

echo $model[0]['attributes']['item_id'];

Link to comment
Share on other sites

Jonathon, your solution sounds close to okay, but I'm not sure about the relationship between the ID value passed to the page and the array indexes. To me, there wouldn't be a relationship, but I'm not clear on the details here.

Link to comment
Share on other sites

Apologies Larry, I should have posted more code

 

My route is

 

 
                'location/<id:\d+>/<slug:[a-z-]+>' => 'area/index', // url for creating location/1/area-name
 

 

My Model

 

 
    public function returnItemsInArea($id){
             
        $model = Item::model()->with('areaItems')->findAll($id);
        
        return $model;
 
    }
 

 

My Controller

 

 public function actionIndex($id)
    {
       
    $items = Item::model()->returnItemsInArea($id);
    $recordCounter = (int)$id -1;
   
        $this->render('index', array(
        'model'=>$items,
        'id'=>$recordCounter
        ));
    }

My thinking was that localhost/location/1/area-name would be a way to select all the items in a particular area by passing the id of area in the url. The id in the url is mentioned in the route, I take that in the controller and pass it to the model to query the DB. But with it returning an array of objects I needed a way to select the correct object dynamically. This seemed like a way to do it. Although I wasn't sure if this was acceptable to do or how it could be done better.

Link to comment
Share on other sites

Here's my concern: arrays are indexed beginning at 0. If you're dealing with ID number 75, then subtracting one to start there in the array of objects won't work, will it? Why not just refer to $models[0]->whatever in your view file?

Link to comment
Share on other sites

Hi Larry

 

I did start with that.

 

<?php echo CHtml::encode($model[0]->area); ?>

 

But that will always print the 1st area from the DB won't it? It seems to.

 

In the area table if the records are like this:

 

1 London

2 Manchester

3 Birmingham

// more areas

 

so the url:

localhost/location/2/manchester

localhost/location/3/birmingham

 

would still print <h1>London Items</h1> when using <?php echo CHtml::encode($model[0]->area); ?>. Because $model[0]->area will always be the first area in the DB (London). 

 

So I needed a way to dynamically print the <h1> tag depending on what value was used in the url.

 

If I passed back the $id unadjusted for manchester area items. Then the $id would be 2. 

 

<h1><?php echo CHtml::encode($model[$id]['areaItems']); ?> Items</h1>

 

Then it would print the 3rd area's title (Birmingham) and the result set would also be that of Birmingham's, not Manchester's.

 

The variable isn't altered from what is provided in the url when accessing the controller or model. So my (flawed) logic was get all the correct records for each area. Then provide an adjusted variable to pass back to the view so you can align the php array with the mysql record so you can correctly print the area's <h1> tag and loop through the returned results?

Link to comment
Share on other sites

I'm sorry Larry I'm not explaining this well.

 

Perhaps this may help:

 

I have an area table like this:

 

 

id

area

date_added

 

I have an items table like this:

 

id

title

description

user_id  (FK To user table)

area_id (FK to area table)

date_added

 

It stores items, so a row might be:

 

id = 1

title = red widget

description = a description of the widget

user = 32

area_id = 3

date_added = 2013-01-12 17:09:19

 

If i were to load an area page so localhost/location/3/birmingham

 

I want it to load all records in the item table that have a FK that equates to birmingham.

 

So it's like finding all the items that are for sale in a particular geographical area.

 

Does that help at all. 

 

EDIT:

 

So

 

$model[$id]['area'] // relates to the area.

and

 

$model[$id]['areaItems'] // relates to the items for that area

areaItems is a relation in my Area Model: 

 

'areaItems' => array(self::HAS_MANY, 'Items', 'area_id'),
Link to comment
Share on other sites

Build you array differently. Store it to a key "records" (or similar) then bind the title the same way. That way, you can echo $title and foreach $records without any problems. If your problem is re-iteration, there's several ways to do that. The easiest way might be calling reset() on $data['records']. (Considering how my keys are arranged.)

Link to comment
Share on other sites

 

I'm sorry Larry I'm not explaining this well.

 

Perhaps this may help:

 

I have an area table like this:

 

 

id

area

date_added

 

I have an items table like this:

 

id

title

description

user_id  (FK To user table)

area_id (FK to area table)

date_added

 

It stores items, so a row might be:

 

id = 1

title = red widget

description = a description of the widget

user = 32

area_id = 3

date_added = 2013-01-12 17:09:19

 

If i were to load an area page so localhost/location/3/birmingham

 

I want it to load all records in the item table that have a FK that equates to birmingham.

 

So it's like finding all the items that are for sale in a particular geographical area.

 

Does that help at all. 

 

EDIT:

 

So

 

$model[$id] // relates to the area.

and

 

$model[$id]['areaItems'] // relates to the items for that area

areaItems is a relation in my Area Model: 

 

'areaItems' => array(self::HAS_MANY, 'Items', 'area_id'),

 

I tried making a relationship to a table holding my countries and the Yii relationships would not work. So for instance if you have a dropdown list of areas and you wanted birmingham the only way i could access it would be find the id of birmingham, then find items that were located in birmingham via the id. I still don't understand why Yii didn't work when i put an id like you did for area id in one table but could not use relational active record, i always got an error no matter how i configured it. I think this is a time we need to use query builder for a simple join.

Link to comment
Share on other sites

Morning Gents,

 

To confirm the process i'm currently using does work. But Larry was questioning my use of the $id variable to help select the right array. So I was just trying to explain it better for him so he could say yeah that's ok, or no, it's a horror show. :lol:

 

Thomas, thanks for your input, I do much prefer using Query builder, but I had problems trying to echo out just the $title within a foreach loop and then accessing the foreach loop again. (CDbDataReader cannot rewind. It is a forward-only reader.). But I shall look into what you say thank you.

 

Edward, I read what you said before and thought, I shouldn't give up on AR so easily so I went back to try and get it to work. It was simple to get the results out I suppose, far less code, but I did find it harder to access as you said you'd found. I prefer QB personally, but the only issue I find with it is you can only use the foreach loop once. Which makes printing the <h1> tag "Birmingham Items" and then listing all the items returned impossible. 

Link to comment
Share on other sites

Right,

 

To update this situation again. I was having a think about it last night. My problem seemed to be that I was picking up all the Areas from the DB when I only needed one. So I went back and altered my model to

 

 

 
        $model = Area::model()->with('areaItems')->find('t.id=:id', array(':id'=>$id));

This only selects the chosen area by an ID value and then all the items that have this location id. Rather than selecting all of the areas and then just filling up each area array with items.

 

So in my view I can now do this

 

<h1>
<?php if(!isset($model->area)){ // $model is NOT returned throw error
	throw new CHttpException(404,'The requested page does not exist.'); 
}else{ // $model is returned continue
	echo CHtml::encode($model->area); 
	} 
?> 
Items</h1>

 

and then iterate through the items in that area if there are any by using the relationship name within the model.

 

// Are there any items in this area?
if (empty($model['areaItems'])): ?>

	<p>Currently there are no Items in the area</p>
    
<?php else: ?>

<?php	
// $model['areaItems'] is NOT empty so items are iterated through
		foreach($model['areaItems'] as $row): ?>
	
            <h2><?php echo $row['title']; ?></h2>
            <p><?php echo $row['descripion']; ?></p>
	
		<?php endforeach; ?>
<?php endif; ?>

 

I removed the $id in the controller now. The controller just calls the model function and passes the returned model to the view.

 

Thanks

 

Jonathon

Link to comment
Share on other sites

 Share

×
×
  • Create New...