/** * Do a programmatic 1:n join between the given objects and objects retrieved from the passed repository. * * Example: * * $productRepository->joinMany($products, $tagsRepository, 'id', 'product_id', 'tags'); * * The above code will query `$tagsRepository` for all tags that have `id`'s of the `$products` in `product_id` * property. Then it will match `$products` and the fetched tags and set all related tags on each product by * calling `::setTags()` with the related tags. * * @param array $objects Objects to which associated objects will be joined. * @param Repository $withRepository Repository from which associated objects should be fetched. * @param string $leftProperty Property of `$objects` under which the relation is stored. * @param string $rightProperty Property on which the fetched objects can be identified. * @param string $targetProperty Property of `$objects` on which the associated object will be set. This is * converted to a camelCase setter. * @param array $criteria [optional] Any additional criteria for finding the associated objects. * @param array $params [optional] Any additional search parameters for finding the associated objects. * @param boolean $excludeEmpty [optional] Should objects that didn't find the match be removed * from the results set? Set as `Knit::EXCLUDE_EMPTY` constant. Default: `false`. * * @return array */ public function joinMany(array $objects, Repository $withRepository, $leftProperty, $rightProperty, $targetProperty, array $criteria = [], array $params = [], $excludeEmpty = false) { // if empty collection then don't even waste time :) if (empty($objects)) { return $objects; } // select all objects for the right side of the join $criteria = array_merge($criteria, [$rightProperty => ObjectUtils::pluck($objects, $leftProperty)]); $withObjects = $withRepository->find($criteria, $params); $withObjects = ObjectUtils::groupBy($withObjects, $rightProperty); // do the programmatic join $getter = ObjectUtils::getter($leftProperty); $setter = ObjectUtils::setter($targetProperty); foreach ($objects as $i => $object) { $match = $object->{$getter}(); if (isset($withObjects[$match])) { $object->{$setter}($withObjects[$match]); } elseif ($excludeEmpty === Knit::EXCLUDE_EMPTY) { unset($objects[$i]); } } return array_values($objects); }
/** * Tests finding objects by various queries. * * @param array $criteria Search criteria. * @param array $params Search params. * @param array $expected Expected results. * * @depends testInsert * @dataProvider provideFindCriteria */ public function testFind(array $criteria, array $params, array $expected) { $this->insertData(); $hobbits = $this->repository->find($criteria, $params); $this->assertCount(count($expected), $hobbits, $this->logger->getLastMessage()); $this->assertEquals($expected, ObjectUtils::pluck($hobbits, 'name'), $this->logger->getLastMessage()); }
public function testSetter() { $this->assertEquals('setId', ObjectUtils::setter('id')); $this->assertEquals('setName', ObjectUtils::setter('name')); $this->assertEquals('setCategoryId', ObjectUtils::setter('categoryId')); $this->assertEquals('setCategoryId', ObjectUtils::setter('category_id')); $this->assertEquals('setVeryLongRandomlySeparatedVariableName', ObjectUtils::setter('veryLong_randomly_Separated_variableName')); }
/** * Tests finding by multiple id's. * * @depends testInsert */ public function testFindByMultipleIds() { $hobbits = $this->repository->find(); $otherHobbits = $this->repository->find(['id:in' => ObjectUtils::pluck($hobbits, 'id')]); $this->assertEquals($hobbits, $otherHobbits, $this->logger->getLastMessage()); }
/** * Tests joining many items to a collection of objects. * * @param array $objects Objects. * @param array $criteria Search criteria. * @param array $params Search params. * @param array $withObjects Search results from the other repository. * @param boolean $excludeEmpty Exclude empty? * @param array $expected Expected result. * * @dataProvider provideJoinManyData */ public function testJoinMany(array $objects, array $criteria, array $params, array $withObjects, $excludeEmpty, array $expected) { $hobbitMocks = $this->provideMocks(); $hobbitRepository = $this->provideRepository($hobbitMocks); $giftMocks = $this->provideMocks(); $giftMocks['objectClass'] = Fixtures\ElvenGift::class; $giftRepository = $this->provideRepositoryStub($giftMocks, ['find']); if (!empty($objects)) { $giftRepository->expects($this->once())->method('find')->with(array_merge($criteria, ['owner_name' => ObjectUtils::pluck($objects, 'name')]), $params)->will($this->returnValue($withObjects)); } else { $giftRepository->expects($this->never())->method('find'); } $result = $hobbitRepository->joinMany($objects, $giftRepository, 'name', 'owner_name', 'gifts', $criteria, $params, $excludeEmpty); $this->assertInternalType('array', $result); $this->assertCount(count($expected), $result); foreach ($result as $hobbit) { $gifts = $hobbit->getGifts(); $expectedGifts = $expected[$hobbit->getName()]; $this->assertCount(count($expectedGifts), $gifts); foreach ($gifts as $i => $gift) { $this->assertEquals($expectedGifts[$i], $gift->getName()); } } }
/** * Interpolates the given string with variables. * * Looks for occurrences of placeholders like `{foo}` or `{bar}` and replaces them with values found under * keys `foo` or `bar` (respectively) in the given array of variables. * * Also accepts an object as the second argument and in such case it will attempt to read * the properties of that object using getters. * * Example: * * echo \MD\Foundation\Utils\StringUtils::interpolate('I have a {item} and I will {action} it.', array( * 'item' => 'banana', * 'action' => 'eat' * )); * // -> 'I have a banana and I will eat it.' * * @param string $string String to interpolate. * @param mixed $variables Either an array or an object with variables to be interpolated into the string. * @return string * * @throws InvalidArgumentException If the `$variables` argument is neither an array or an object. */ public static function interpolate($string, $variables) { if (!is_object($variables) && !is_array($variables)) { throw new InvalidArgumentException('array or object', $variables); } $string = preg_replace_callback('/{([\\w\\d_\\.]+)}/is', function ($matches) use($variables) { $var = $matches[1]; $value = ''; if (is_object($variables)) { $getter = ObjectUtils::getter($var); if (method_exists($variables, $getter)) { $value = strval(call_user_func(array($variables, $getter))); } elseif (isset($variables->{$var})) { $value = strval($variables->{$var}); } } elseif (is_array($variables)) { if (isset($variables[$var])) { $value = strval($variables[$var]); } } return $value; }, $string); return $string; }
/** * Get the property. It will try to call a defined getter first. * * If the property does not exist then it will trigger an `E_USER_NOTICE`. * * use MD\Foundation\MagicUtils; * * class MyClass extends MagicUtils * { * protected $full_title = 'Lorem ipsum dolor sit amet.'; * * public function getFullTitle() * { * echo 'Getting full title "'. $this->full_title .'"'; * return $this->full_title; * } * } * * $object = new MyClass(); * echo $object->full_title; * // -> 'Getting full title "Lorem ipsum dolor sit amet."' * * @param string $property Name of the property. * @return mixed */ public final function __get($property) { // try to call a defined getter if it exists $getter = ObjectUtils::getter($property); if (method_exists($this, $getter)) { return call_user_func(array($this, $getter)); } // if no getter then simply return the property if it exists if (array_key_exists($property, $this->__properties)) { return $this->__properties[$property]; } // trigger a user notice if property not found return trigger_error('Call to undefined object property ' . get_called_class() . '::$' . $property . '.', E_USER_NOTICE); }