Question

I have a table called items and a table called item_pics.

item_pics has an item_id, file_name and a rank field (among others).


What I'm looking for is for each item my index page's $items array to contain the file_name from the item_pics matching the item's item_id with the lowest rank. So I can access like (or something like) this in my Items/index.ctp:

 foreach ($items as $item):
    $img = $item['Item']['ItemPic']['file_name'];
    ...

I'm pretty new to CakePHP, this is my first project. I thought that this within the Item model would cause item_pics data to be pulled (although I figured all related item_pics for each item would get pulled rather than just the one with the lowest rank):

public $hasMany = array(
    'ItemPic' => array(
        'className' => 'ItemPic',
        'foreignKey' => 'item_id',
        'dependent' => false
    )
}

but I can see that no item_pics data is loaded (at the bottom of items/index):

SELECT `Item`.`id`, `Item`.`title`, `Item`.`description`, `Item`.`created`, `Item`.`modified`, `Item`.`type`, `Project`.`id`, `Project`.`item_id`, `Project`.`title`, `Project`.`description`, `Project`.`rank`, `Project`.`created`, `Project`.`modified`
FROM `laurensabc`.`items` AS `Item`
LEFT JOIN `laurensabc`.`projects`
AS `Project`
ON (`Project`.`item_id` = `Item`.`id`)
WHERE `Item`.`type` IN (1, 2)
LIMIT 20

also, while I would like projects to be joined in the view pages, I don't really need them in the index page.

I've done some searching and haven't been able to find exactly what I'm looking for. I suppose I could do a query within the index view item loop, but I'm trying to make sure I do things the right way... the CakePHP way. I assume I need to change something about my model relationships but I haven't had any luck.

CakePHP - Associations - HasMany, this makes it seem like I could order by rank and limit 1. But this didn't work... and even if it did, I wouldn't want that to affect the view pages but rather just the index page.

My Controller looks like this:

public function index($type = null) {
    $this->Item->recursive = 0;
    $conditions = array();
    if ($type == "sale") {
        $conditions = array(
                "Item.type" => array(self::FOR_SALE, self::FOR_SALE_OR_RENT)
        );
    } else if ($type == "rent" ) {
        $conditions = array(
                "Item.type" => array(self::FOR_RENT, self::FOR_SALE_OR_RENT)
        );
    } else {
        $conditions = array("Item.type !=" => self::HIDDEN);
    }
    $paginated = $this->Paginator->paginate($conditions);
    debug($paginated);
    $this->set('items', $paginated);
    $this->set('title', ($type == null ? "Items for Sale or Rent" : "Items for " . ucwords($type)));
}

I have also tried this on my controller, but it doesn't seem to do anything either:

    $this->paginate = array(
        'conditions' => $conditions,
        'joins' => array(
            array(
                'alias' => 'ItemPic',
                'table' => 'item_pics',
                'type' => 'left',
                'conditions' => array('ItemPic.item_id' => 'Item.id'),
                'order' => array('ItemPic.rank' => 'asc'),
                'limit' => 1
            )
        )
    );
    $paginated = $this->paginate($this->Item);
Was it helpful?

Solution

First, set containable behavior in AppModel (or if you don't want it on each model, put it on Item model):

public $actsAs = array('Containable');

Then, on your find query:

$items = $this->Item->find('all', array(
    'contain' => array(
        'ItemPic' => array(
            'fields' => array('file_name'),
            'order' => 'rank',
            'limit' => 1
        )
    )
));

Then the result array you can access it like:

foreach ($items as $item):
    $img = $item['ItemPic']['file_name'];

Edit: Then you should put it on the paginate query:

$this->paginate = array(
    'conditions' => $conditions,
    'contain' => array(
        'ItemPic' => array(
            'fields' => array('file_name'),
            'order' => 'rank',
            'limit' => 1
        )
    )
);

OTHER TIPS

In this case, I would probably order by rank and limit 1 as you said, and make that a dynamic association just for the index page (See http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly). So use $this->Item->bindModel(array('hasMany' => array('ItemPic' => $options))); (which I believe should replace your current settings for HasMany ItemPic, but you may have to unbindmodel first)

Associations created through bindModel will go through for the next query only, then it'll revert to your normal settings, unless you specifically set an option to keep using the new association.

As for why it's not getting ItemPics with Items, or why trying to order by rank and limit 1 didn't work for you, I can't really say without seeing more of your code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top