9

I'm using Propel 1.6 and I'm not sure how to get an object (given its "id" attribute value) from a propel object collection. I could not find a straight answer in Propel's documentation (PropelCollection methods do not seem applicable?). For example: Lets say I have a "Person" table with the following schema:

<table name="person">
  <column name="id" type="INTEGER" primaryKey="true" autoIncrement="true" required="true"/>
  <column name="name" type="VARCHAR" size="100" required="true"/>
</table>

I do the following query to get a collection of "Person" objects:

$persons = PersonQuery::create()->find();

Then, I want to find a "Person" object with a given "id" (e.g. "Person" with "id=3"), without making a new query to the database. How can I do it?

$persons->get(...?)?

In other words, I DO NOT want to do:

$personX = PersonQuery::create()->findOneById(3);

Context:

I would like to prevent making a database query to improve performance. The statement is to be inserted inside a foreach statement that would otherwise lead to numerous database connections, like the following:

foreach ($books as $book) {
    $book['author_name'] = PersonQuery::create()->findOneById($book['author_id'])->getName();
}
RayOnAir
  • 2,038
  • 2
  • 22
  • 33

5 Answers5

9

Another alternative, especially if you need to search several times is to get array of objects by id with $collection->getArrayCopy('Id').

$persons = PersonQuery::create()->find();
$personsById = $persons->getArrayCopy('Id');

Then you can do

$person = $personsById[3];

or

if (isset($personsById[3])) {
  $person = $personsById[3];
  ...
}
Ventzy Kunev
  • 196
  • 1
  • 4
4

Well, that won't be very efficient, but you can go through the collection to find it.

$persons = PersonQuery::create()->find();
foreach ($persons as $person)
{
  if (3 == $person->getId())
  {
    break;
  }
}

// now you have your person with id = 3
var_dump($person->getId());
j0k
  • 22,600
  • 28
  • 79
  • 90
  • 1
    You can do it in a more elegant way with `array_filter()` :-) – Florent Sep 03 '12 at 16:42
  • @j0k: I was trying to avoid a foreach loop, but I guess, as you suggest, I cannot avoid it. I think going with the loop would still be more efficient than making multiple database connections. Thanks for your answer! – RayOnAir Sep 03 '12 at 17:22
  • 1
    @Florent: I'm not sure how to implement the [array_filter](http://php.net/manual/en/function.array-filter.php) function in this case, but I'll investigate... thanks! – RayOnAir Sep 03 '12 at 17:28
3

Since Propel don't cache properly query result, you need to iterate the collection (as @j0k said). Instead of using a foreach loop, you can call array_filter passing a closure (with PHP 5.3).

// Request the persons
$persons = PersonQuery::create()->find();

// Filter the persons whose ID equals 3
$filteredPersons = array_filter($persons, function ($person) {
    return 3 === $person->getId();
});

// Get the first result
$person = empty($filteredPersons) ? null : $filteredPersons[0];

If you are sure that the person will be found, you also can write (with PHP 5.4) the following lines:

// Filter the person whose ID equals 3
$person = array_filter($persons, function ($person) {
    return 3 === $person->getId();
})[0];
Florent
  • 12,310
  • 10
  • 49
  • 58
  • thanks for sharing an alternative solution using array_filter! Would this solution be better from a performance stand point, as compared to using a foreach loop? If so, could you share the intuition behind? – RayOnAir Sep 04 '12 at 13:46
  • 1
    With PHP 5.4, `foreach` is 3x faster than `array_filter()`. Moreover using `foreach` you can `break` when your entry is found. But in my opinion `array_filter()` is more _aesthetic_. – Florent Sep 04 '12 at 15:50
2

if you set Propel::isInstancePoolingEnabled() is true(it's true by default),then you can

// Request the persons
$persons = PersonQuery::create()->find();
// get person from pool
$person = PersonPeer::getInstanceFromPool(3);

sorry about my english.

1

The alternative with Propel >=1.5 to user2663223's answer would be:

$persons = PersonQuery::create()->find()

$person = PersonQuery::create()->findPk(3);

This takes advantage of the instance pool. Internally it uses: getInstanceFromPool, if the sql query was done before.

For more info, consult:

http://propelorm.org/documentation/03-basic-crud.html#propel-instance-pool

netadictos
  • 7,602
  • 2
  • 42
  • 69