diff --git a/.travis.yml b/.travis.yml index 2e60a91..b517e88 100755 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ matrix: before_script: - travis_retry composer self-update - travis_retry composer install --prefer-source --no-interaction + - travis_retry composer update --no-interaction --prefer-source - if [ "$LARAVEL_VERSION" != "" ]; then composer require --dev "laravel/laravel:${LARAVEL_VERSION}" --no-update; fi; - - composer update script: - vendor/bin/phpunit diff --git a/src/Commands/Store.php b/src/Commands/Store.php index ca097e0..585ba8c 100755 --- a/src/Commands/Store.php +++ b/src/Commands/Store.php @@ -150,7 +150,7 @@ protected function postStoreProcess() // Now we can sync the related collections // - // TODO (note) : not sute this check is needed, as we can assume + // TODO (note) : not sure this check is needed, as we can assume // the aggregate exists in the Post Store Process if ($this->aggregate->exists()) { $this->aggregate->syncRelationships($foreignRelationships); @@ -186,6 +186,10 @@ protected function insert() unset($attributes[$keyName]); } + if (isset($attributes['attributes'])) { + unset($attributes['attributes']); + } + $id = $this->query->insertGetId($attributes, $keyName); $aggregate->setEntityAttribute($keyName, $id); diff --git a/src/EntityMap.php b/src/EntityMap.php index 2f6cc6b..bfb5a5b 100755 --- a/src/EntityMap.php +++ b/src/EntityMap.php @@ -499,7 +499,7 @@ public function getProperties(): array * * @return array */ - public function getEvents() : array + public function getEvents(): array { return $this->events; } @@ -510,7 +510,7 @@ public function getEvents() : array * * @return string */ - public function getAttributesPropertyName() : string + public function getAttributesPropertyName(): string { } @@ -1572,10 +1572,7 @@ public function getMorphClass(): string */ public function newCollection(array $entities = []): EntityCollection { - $collection = new EntityCollection($entities); - $keyName = $this->getAttributeNameForColumn($this->getKeyName()); - - return $collection->keyBy($keyName); + return new EntityCollection($entities); } /** @@ -1745,6 +1742,7 @@ public function getAttributeNamesFromColumns($array) return $newArray; } if ($this->camelCaseHydratation) { + $newArray = []; foreach ($array as $key => $value) { $attributeName = camel_case($key); $newArray[$attributeName] = $value; @@ -1796,6 +1794,7 @@ public function getColumnNamesFromAttributes($array) return $newArray; } if ($this->camelCaseHydratation) { + $newArray = []; foreach ($array as $key => $value) { $attributeName = snake_case($key); $newArray[$attributeName] = $value; diff --git a/src/Plugins/SoftDeletes/SoftDeletesPlugin.php b/src/Plugins/SoftDeletes/SoftDeletesPlugin.php index 68a671e..9549c4e 100755 --- a/src/Plugins/SoftDeletes/SoftDeletesPlugin.php +++ b/src/Plugins/SoftDeletes/SoftDeletesPlugin.php @@ -91,7 +91,7 @@ protected function registerSoftDelete(Mapper $mapper) * * @return InternallyMappable */ - protected function getMappable($entity) : InternallyMappable + protected function getMappable($entity): InternallyMappable { if ($entity instanceof InternallyMappable) { return $entity; @@ -106,7 +106,7 @@ protected function getMappable($entity) : InternallyMappable /** * {@inheritdoc} */ - public function getCustomEvents(): array + public function getCustomEvents(): array { return [ 'restoring' => Events\Restoring::class, diff --git a/src/Plugins/Timestamps/TimestampsPlugin.php b/src/Plugins/Timestamps/TimestampsPlugin.php index 6b3b058..5d8bbbe 100755 --- a/src/Plugins/Timestamps/TimestampsPlugin.php +++ b/src/Plugins/Timestamps/TimestampsPlugin.php @@ -66,7 +66,7 @@ public function register() * * @return InternallyMappable */ - protected function getMappable($entity) : InternallyMappable + protected function getMappable($entity): InternallyMappable { if ($entity instanceof InternallyMappable) { return $entity; diff --git a/src/Relationships/BelongsToMany.php b/src/Relationships/BelongsToMany.php index 0bcee82..9581653 100755 --- a/src/Relationships/BelongsToMany.php +++ b/src/Relationships/BelongsToMany.php @@ -196,7 +196,7 @@ public function firstOrFail($columns = ['*']) * * @return \Illuminate\Support\Collection */ - public function get($columns = ['*']) : Collection + public function get($columns = ['*']): Collection { // First we'll add the proper select columns onto the query so it is run with // the proper columns. Then, we will get the results and hydrate out pivot @@ -205,7 +205,7 @@ public function get($columns = ['*']) : Collection $select = $this->getSelectColumns($columns); - $entities = $this->query->addSelect($select)->get()->all(); + $entities = $this->query->addSelect($select)->disableCache()->get()->all(); $entities = $this->hydratePivotRelation($entities); @@ -217,28 +217,29 @@ public function get($columns = ['*']) : Collection * * @param array $entities * - * @return void + * @return array */ protected function hydratePivotRelation(array $entities) { // TODO (note) We should definitely get rid of the pivot in a next // release, as this is not quite relevant in a datamapper context. - $host = $this; - - return array_map(function ($entity) use ($host) { + return array_map(function ($entity) { $entityWrapper = $this->factory->make($entity); - $pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper)); + $pivotAttributes = $this->cleanPivotAttributes($entityWrapper); + $pivot = $this->newExistingPivot($pivotAttributes); $entityWrapper->setEntityAttribute('pivot', $pivot); - return $entityWrapper->getObject(); + $object = $entityWrapper->unwrap(); + + return $object; }, $entities); } /** * Get the pivot attributes from a model. * - * @param $entity + * @param InternallyMappable $entity * * @return array */ @@ -442,7 +443,9 @@ public function match(array $results, $relation) // children back to their parent using the dictionary and the keys on the // the parent models. Then we will return the hydrated models back out. return array_map(function ($result) use ($dictionary, $keyName, $cache, $relation, $host) { - if (isset($dictionary[$key = $result[$keyName]])) { + $key = $result[$keyName]; + + if (isset($dictionary[$key])) { $collection = $host->relatedMap->newCollection($dictionary[$key]); $result[$relation] = $collection; @@ -475,7 +478,10 @@ protected function buildDictionary(EntityCollection $results) foreach ($results as $entity) { $wrapper = $this->factory->make($entity); - $dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity; + + $foreignKey = $wrapper->getEntityAttribute('pivot')->$foreign; + + $dictionary[$foreignKey][] = $entity; } return $dictionary; @@ -614,8 +620,6 @@ protected function detachExcept(array $entities = []) $query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey)); $query->delete(); - - $query = $this->newPivotQuery(); } /** diff --git a/src/Relationships/EmbeddedRelationship.php b/src/Relationships/EmbeddedRelationship.php index 8eabb0e..60e98ba 100644 --- a/src/Relationships/EmbeddedRelationship.php +++ b/src/Relationships/EmbeddedRelationship.php @@ -138,7 +138,7 @@ public function setPrefix(string $prefix) * * @return string */ - public function getPrefix() : string + public function getPrefix(): string { return $this->prefix; } @@ -149,7 +149,7 @@ public function getPrefix() : string * * @return array */ - protected function getEmbeddedObjectAttributes() : array + protected function getEmbeddedObjectAttributes(): array { $entityMap = $this->getRelatedMapper()->getEntityMap(); @@ -166,7 +166,7 @@ protected function getEmbeddedObjectAttributes() : array * * @return string */ - protected function getParentAttributeKey($key) : string + protected function getParentAttributeKey($key): string { return $this->getPrefixedAttributeKey($this->getMappedParentAttribute($key)); } @@ -179,7 +179,7 @@ protected function getParentAttributeKey($key) : string * * @return string */ - protected function getMappedParentAttribute(string $key) : string + protected function getMappedParentAttribute(string $key): string { if (array_key_exists($key, $this->columnMap)) { return $this->columnMap[$key]; @@ -195,7 +195,7 @@ protected function getMappedParentAttribute(string $key) : string * * @return string */ - protected function getPrefixedAttributeKey(string $attributeKey) : string + protected function getPrefixedAttributeKey(string $attributeKey): string { return $this->prefix.$attributeKey; } @@ -206,7 +206,7 @@ protected function getPrefixedAttributeKey(string $attributeKey) : string * * @return array */ - abstract public function match(array $results) : array; + abstract public function match(array $results): array; /** * Build an embedded object instance. @@ -217,7 +217,7 @@ abstract public function match(array $results) : array; */ protected function buildEmbeddedObject(array $attributes) { - $resultBuilder = new ResultBuilder($this->getRelatedMapper()); + $resultBuilder = new ResultBuilder($this->getRelatedMapper(), true); // TODO : find a way to support eager load within an embedded // object. @@ -233,7 +233,7 @@ protected function buildEmbeddedObject(array $attributes) * * @return array $columns */ - abstract public function normalize($object) : array; + abstract public function normalize($object): array; /** * Return parent mapper. diff --git a/src/Relationships/EmbedsMany.php b/src/Relationships/EmbedsMany.php index 1182185..a7e8ad1 100644 --- a/src/Relationships/EmbedsMany.php +++ b/src/Relationships/EmbedsMany.php @@ -17,7 +17,7 @@ class EmbedsMany extends EmbedsOne * * @return array */ - public function matchSingleResult(array $attributes) : array + public function matchSingleResult(array $attributes): array { $column = $this->relation; @@ -38,7 +38,7 @@ public function matchSingleResult(array $attributes) : array * * @return array */ - protected function matchAsArray(array $attributes) : array + protected function matchAsArray(array $attributes): array { // Extract the attributes with the key of the relation, // which should be an array. @@ -84,7 +84,7 @@ protected function buildEmbeddedCollection(array $rows): Collection * * @return array $columns */ - public function normalize($objects) : array + public function normalize($objects): array { if (!$this->asArray) { throw new MappingException('Cannot normalize an embedsMany relation as row columns'); @@ -102,7 +102,7 @@ public function normalize($objects) : array * * @return array */ - protected function normalizeAsArray($objects) : array + protected function normalizeAsArray($objects): array { $key = $this->relation; diff --git a/src/Relationships/EmbedsOne.php b/src/Relationships/EmbedsOne.php index 4ef2c7f..02729bf 100644 --- a/src/Relationships/EmbedsOne.php +++ b/src/Relationships/EmbedsOne.php @@ -21,7 +21,7 @@ class EmbedsOne extends EmbeddedRelationship * * @return array */ - public function match(array $results) : array + public function match(array $results): array { return array_map([$this, 'matchSingleResult'], $results); } @@ -34,7 +34,7 @@ public function match(array $results) : array * * @return array */ - public function matchSingleResult(array $attributes) : array + public function matchSingleResult(array $attributes): array { return $this->asArray ? $this->matchAsArray($attributes) : $this->matchAsAttributes($attributes); } @@ -49,7 +49,7 @@ public function matchSingleResult(array $attributes) : array * * @return array */ - protected function matchAsArray(array $attributes) : array + protected function matchAsArray(array $attributes): array { // Extract the attributes with the key of the relation, // which should be an array. @@ -76,7 +76,7 @@ protected function matchAsArray(array $attributes) : array * * @return array */ - protected function matchAsAttributes(array $attributes) : array + protected function matchAsAttributes(array $attributes): array { $attributesMap = $this->getAttributesDictionnary(); @@ -108,7 +108,7 @@ protected function matchAsAttributes(array $attributes) : array * * @return array */ - protected function getAttributesDictionnary() : array + protected function getAttributesDictionnary(): array { // Get attributes that belongs to the embedded object $embeddedAttributeKeys = $this->getEmbeddedObjectAttributes(); @@ -130,7 +130,7 @@ protected function getAttributesDictionnary() : array * * @return array $columns */ - public function normalize($object) : array + public function normalize($object): array { return $this->asArray ? $this->normalizeAsArray($object) : $this->normalizeAsAttributes($object); } @@ -142,7 +142,7 @@ public function normalize($object) : array * * @return array */ - protected function normalizeAsArray($object) : array + protected function normalizeAsArray($object): array { $wrapper = $this->factory->make($object); @@ -162,7 +162,7 @@ protected function normalizeAsArray($object) : array * * @return array */ - protected function normalizeAsAttributes($object) : array + protected function normalizeAsAttributes($object): array { if (is_null($object)) { return $this->nullObjectAttributes(); @@ -186,7 +186,7 @@ protected function normalizeAsAttributes($object) : array * * @return array */ - protected function nullObjectAttributes() : array + protected function nullObjectAttributes(): array { $attributesMap = $this->getAttributesDictionnary(); diff --git a/src/Relationships/HasManyThrough.php b/src/Relationships/HasManyThrough.php index b03ddce..980560e 100755 --- a/src/Relationships/HasManyThrough.php +++ b/src/Relationships/HasManyThrough.php @@ -6,6 +6,7 @@ use Analogue\ORM\System\Mapper; use Analogue\ORM\System\Query; use Illuminate\Database\Query\Expression; +use Illuminate\Support\Collection; class HasManyThrough extends Relationship { @@ -217,7 +218,7 @@ public function getResults($relation) * * @return EntityCollection */ - public function get($columns = ['*']) + public function get($columns = ['*']): Collection { // First we'll add the proper select columns onto the query so it is run with // the proper columns. Then, we will get the results and hydrate out pivot diff --git a/src/System/Aggregate.php b/src/System/Aggregate.php index 6c6f21d..4d6a946 100755 --- a/src/System/Aggregate.php +++ b/src/System/Aggregate.php @@ -1065,7 +1065,7 @@ protected function getRelatedAggregateFromHash(string $hash, string $relation) */ protected function getEntityHashesFromRelation(string $relation): array { - return array_map(function (Aggregate $aggregate) { + return array_map(function (self $aggregate) { return $aggregate->getEntityHash(); }, $this->relationships[$relation]); } diff --git a/src/System/Builders/EntityBuilder.php b/src/System/Builders/EntityBuilder.php index 4c6c5ab..4584337 100644 --- a/src/System/Builders/EntityBuilder.php +++ b/src/System/Builders/EntityBuilder.php @@ -2,6 +2,7 @@ namespace Analogue\ORM\System\Builders; +use Analogue\ORM\System\InternallyMappable; use Analogue\ORM\System\Mapper; use Analogue\ORM\System\Wrappers\Factory; @@ -43,13 +44,19 @@ class EntityBuilder */ protected $factory; + /** + * @var bool + */ + protected $useCache; + /** * EntityBuilder constructor. * * @param Mapper $mapper * @param array $eagerLoads + * @param bool $useCache */ - public function __construct(Mapper $mapper, array $eagerLoads) + public function __construct(Mapper $mapper, array $eagerLoads, bool $useCache = false) { $this->mapper = $mapper; @@ -58,6 +65,8 @@ public function __construct(Mapper $mapper, array $eagerLoads) $this->eagerLoads = $eagerLoads; $this->factory = new Factory(); + + $this->useCache = $useCache; } /** @@ -65,13 +74,13 @@ public function __construct(Mapper $mapper, array $eagerLoads) * * @param array $attributes * - * @return array + * @return mixed */ public function build(array $attributes) { // If the object we are building is a value object, // we won't be using the instance cache. - if ($this->entityMap->getKeyName() === null) { + if (!$this->useCache || $this->entityMap->getKeyName() === null) { return $this->buildEntity($attributes); } diff --git a/src/System/Builders/PolymorphicResultBuilder.php b/src/System/Builders/PolymorphicResultBuilder.php index 14d04b0..5a95ed6 100644 --- a/src/System/Builders/PolymorphicResultBuilder.php +++ b/src/System/Builders/PolymorphicResultBuilder.php @@ -104,7 +104,7 @@ protected function buildResultsForType($results, $type, array $eagerLoads) return $builder->build($results, $eagerLoads); } - protected function getMapperForType(string $type) : Mapper + protected function getMapperForType(string $type): Mapper { $columnMap = $this->entityMap->getDiscriminatorColumnMap(); diff --git a/src/System/Builders/ResultBuilder.php b/src/System/Builders/ResultBuilder.php index fe9bfe7..71456d4 100644 --- a/src/System/Builders/ResultBuilder.php +++ b/src/System/Builders/ResultBuilder.php @@ -37,21 +37,30 @@ class ResultBuilder implements ResultBuilderInterface */ protected $builders = []; + /** + * Whether to use result and entity caching. + * + * @var bool + */ + protected $useCache; + /** * ResultBuilder constructor. * - * @param Mapper $mapper + * @param Mapper $mapper The mapper used to build entities with. + * @param bool $useCache [optional] Whether to use result and entity caching. Defaults to false. */ - public function __construct(Mapper $mapper) + public function __construct(Mapper $mapper, bool $useCache = false) { $this->mapper = $mapper; $this->entityMap = $mapper->getEntityMap(); + $this->useCache = $useCache; } /** * Convert a result set into an array of entities. * - * @param array $results + * @param array $results The results to convert into entities. * @param array $eagerLoads name of the relation(s) to be eager loaded on the Entities * * @return array @@ -82,9 +91,10 @@ public function build(array $results, array $eagerLoads) protected function cacheResults(array $results) { $mapper = $this->mapper; + // When hydrating EmbeddedValue object, they'll likely won't // have a primary key set. - if (!is_null($mapper->getEntityMap()->getKeyName())) { + if ($mapper->getEntityMap()->getKeyName() !== null) { $mapper->getEntityCache()->add($results); } } @@ -96,7 +106,7 @@ protected function cacheResults(array $results) * * @return array */ - protected function buildEmbeddedRelationships(array $results) : array + protected function buildEmbeddedRelationships(array $results): array { $entityMap = $this->entityMap; $instance = $this->mapper->newInstance(); @@ -117,7 +127,7 @@ protected function buildEmbeddedRelationships(array $results) : array * * @return array */ - protected function queryEagerLoadedRelationships(array $results, array $eagerLoads) : array + protected function queryEagerLoadedRelationships(array $results, array $eagerLoads): array { $this->eagerLoads = $this->parseRelations($eagerLoads); @@ -221,7 +231,7 @@ public function eagerLoadRelations(array $results): array * * @return array */ - protected function loadRelation(array $results, string $name, Closure $constraints) : array + protected function loadRelation(array $results, string $name, Closure $constraints): array { // First we will "back up" the existing where conditions on the query so we can // add our eager constraints. Then we will merge the wheres that were on the @@ -245,7 +255,7 @@ protected function loadRelation(array $results, string $name, Closure $constrain * * @return \Analogue\ORM\Relationships\Relationship */ - public function getRelation(string $relation) : Relationship + public function getRelation(string $relation): Relationship { // We want to run a relationship query without any constrains so that we will // not have to remove these where clauses manually which gets really hacky @@ -313,11 +323,7 @@ protected function isNested(string $name, string $relation): bool */ protected function buildResultSet(array $results): array { - $keyName = $this->entityMap->getKeyName(); - - return $keyName - ? $this->buildKeyedResultSet($results, $keyName) - : $this->buildUnkeyedResultSet($results); + return $this->buildUnkeyedResultSet($results); } /** @@ -327,9 +333,9 @@ protected function buildResultSet(array $results): array * * @return array */ - protected function buildUnkeyedResultSet(array $results) : array + protected function buildUnkeyedResultSet(array $results): array { - $builder = new EntityBuilder($this->mapper, array_keys($this->eagerLoads)); + $builder = new EntityBuilder($this->mapper, array_keys($this->eagerLoads), $this->useCache); return array_map(function ($item) use ($builder) { return $builder->build($item); @@ -344,9 +350,9 @@ protected function buildUnkeyedResultSet(array $results) : array * * @return array */ - protected function buildKeyedResultSet(array $results, string $primaryKey) : array + protected function buildKeyedResultSet(array $results, string $primaryKey): array { - $builder = new EntityBuilder($this->mapper, array_keys($this->eagerLoads)); + $builder = new EntityBuilder($this->mapper, array_keys($this->eagerLoads), $this->useCache); $keys = array_map(function ($item) use ($primaryKey) { return $item[$primaryKey]; diff --git a/src/System/Builders/ResultBuilderFactory.php b/src/System/Builders/ResultBuilderFactory.php index fa147c2..4faa46f 100644 --- a/src/System/Builders/ResultBuilderFactory.php +++ b/src/System/Builders/ResultBuilderFactory.php @@ -6,13 +6,13 @@ class ResultBuilderFactory { - public function make(Mapper $mapper) : ResultBuilderInterface + public function make(Mapper $mapper, bool $skipCache = false): ResultBuilderInterface { switch ($mapper->getEntityMap()->getInheritanceType()) { case 'single_table': return new PolymorphicResultBuilder($mapper); default: - return new ResultBuilder($mapper); + return new ResultBuilder($mapper, !$skipCache); } } } diff --git a/src/System/Builders/ResultBuilderInterface.php b/src/System/Builders/ResultBuilderInterface.php index 8f88b50..d2b2fee 100644 --- a/src/System/Builders/ResultBuilderInterface.php +++ b/src/System/Builders/ResultBuilderInterface.php @@ -4,5 +4,13 @@ interface ResultBuilderInterface { + /** + * Convert a result set into an array of entities. + * + * @param array $results The results to convert into entities. + * @param array $eagerLoads Relationships to eagerly load for these results. + * + * @return mixed + */ public function build(array $results, array $eagerLoads); } diff --git a/src/System/Manager.php b/src/System/Manager.php index 5d523e5..ffede42 100755 --- a/src/System/Manager.php +++ b/src/System/Manager.php @@ -276,7 +276,7 @@ protected function buildMapper($entity, $entityMap) * * @return bool */ - protected function isAnonymous($class) : bool + protected function isAnonymous($class): bool { $instance = new ReflectionClass($class); diff --git a/src/System/Mapper.php b/src/System/Mapper.php index 7e5d270..b40797f 100755 --- a/src/System/Mapper.php +++ b/src/System/Mapper.php @@ -126,12 +126,13 @@ public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher * * @param array|Collection $results * @param array $eagerLoads + * @param bool $useCache * * @return Collection */ - public function map($results, array $eagerLoads = []) : Collection + public function map($results, array $eagerLoads = [], $useCache = false): Collection { - $builder = $this->newResultBuilder(); + $builder = $this->newResultBuilder(!$useCache); if ($results instanceof Collection) { // Get underlying collection array @@ -159,13 +160,15 @@ public function map($results, array $eagerLoads = []) : Collection /** * Return result builder used by this mapper. * - * @return ResultBuilder + * @param bool $skipCache + * + * @return ResultBuilderInterface */ - protected function newResultBuilder() : ResultBuilderInterface + protected function newResultBuilder(bool $skipCache = false): ResultBuilderInterface { $factory = new ResultBuilderFactory(); - return $factory->make($this); + return $factory->make($this, $skipCache); } /** @@ -358,7 +361,7 @@ public function getEntityMap() * * @return \Analogue\ORM\System\Cache\AttributeCache */ - public function getEntityCache() : AttributeCache + public function getEntityCache(): AttributeCache { return $this->cache; } @@ -368,7 +371,7 @@ public function getEntityCache() : AttributeCache * * @return \Analogue\ORM\System\Cache\InstanceCache */ - public function getInstanceCache() : InstanceCache + public function getInstanceCache(): InstanceCache { return $this->instances; } diff --git a/src/System/Proxies/CollectionProxy.php b/src/System/Proxies/CollectionProxy.php index 76ed08f..179f390 100644 --- a/src/System/Proxies/CollectionProxy.php +++ b/src/System/Proxies/CollectionProxy.php @@ -75,7 +75,7 @@ public function getAddedItems() * * @return bool true if the proxy could be initialized */ - public function initializeProxy() : bool + public function initializeProxy(): bool { if ($this->isProxyInitialized()) { return true; @@ -94,7 +94,7 @@ public function initializeProxy() : bool * * @return Relationship */ - protected function getRelationshipInstance() : Relationship + protected function getRelationshipInstance(): Relationship { $relation = $this->relationshipMethod; $entity = $this->parentEntity; @@ -106,7 +106,7 @@ protected function getRelationshipInstance() : Relationship /** * {@inheritdoc} */ - public function isProxyInitialized() : bool + public function isProxyInitialized(): bool { return $this->relationshipLoaded; } @@ -114,7 +114,7 @@ public function isProxyInitialized() : bool /** * {@inheritdoc} */ - protected function toBaseCollection() : Collection + protected function toBaseCollection(): Collection { return new Collection($this->items); } @@ -1233,7 +1233,7 @@ public function count() * * @return int */ - protected function countUsingDatabaseQuery() : int + protected function countUsingDatabaseQuery(): int { return $this->getRelationshipInstance()->count(); } diff --git a/src/System/Query.php b/src/System/Query.php index f0b721d..35049d4 100755 --- a/src/System/Query.php +++ b/src/System/Query.php @@ -98,6 +98,13 @@ class Query 'raw', ]; + /** + * Whether to use the mapper's entity caching. + * + * @var bool + */ + protected $useCache = true; + /** * Create a new Analogue Query Builder instance. * @@ -125,7 +132,7 @@ public function __construct(Mapper $mapper, DBAdapter $adapter) * * @return \Illuminate\Support\Collection */ - public function get($columns = ['*']) : Collection + public function get($columns = ['*']): Collection { return $this->getEntities($columns); } @@ -503,6 +510,32 @@ protected function getHasRelationQuery($relation, $entity) }); } + /** + * Disable loading results from the entity instance cache. + * + * Loaded entities will still be stored in the cache. + * + * @return \Analogue\ORM\System\Query + */ + public function disableCache() + { + $this->useCache = false; + + return $this; + } + + /** + * Enable loading results from the entity instance cache. + * + * @return \Analogue\ORM\System\Query + */ + public function enableCache() + { + $this->useCache = true; + + return $this; + } + /** * Get the table for the current query object. * @@ -577,7 +610,7 @@ public function getEntities($columns = ['*']) $results = $this->query->get($columns); // Pass result set to the mapper and return the EntityCollection - return $this->mapper->map($results, $this->getEagerLoads()); + return $this->mapper->map($results, $this->getEagerLoads(), $this->useCache); } /** diff --git a/src/System/Wrappers/Factory.php b/src/System/Wrappers/Factory.php index 34a80fc..58afb20 100755 --- a/src/System/Wrappers/Factory.php +++ b/src/System/Wrappers/Factory.php @@ -14,15 +14,16 @@ class Factory * * @throws \Analogue\ORM\Exceptions\MappingException * - * @return Wrapper + * @return ObjectWrapper */ public function make($object) { $manager = Manager::getInstance(); - // Instantiate hydrator. We'll need to optimize this and allow pre-generation - // of these hydrator, and get it, ideally, from the entityMap or the Mapper class, - // so it's only instantiated once + // Instantiate hydrator + // TODO: We'll need to optimize this and allow pre-generation + // of these hydrators, and get it, ideally, from the entityMap or + // the Mapper class, so it's only instantiated once $config = new Configuration(get_class($object)); $hydratorClass = $config->createFactory()->getHydratorClass(); diff --git a/src/System/Wrappers/ObjectWrapper.php b/src/System/Wrappers/ObjectWrapper.php index 7d40570..9acc567 100644 --- a/src/System/Wrappers/ObjectWrapper.php +++ b/src/System/Wrappers/ObjectWrapper.php @@ -75,6 +75,8 @@ class ObjectWrapper implements InternallyMappable * @param mixed $entity * @param \Analogue\ORM\EntityMap $entityMap * @param HydratorInterface $hydrator + * + * @throws MappingException */ public function __construct($entity, $entityMap, HydratorInterface $hydrator) { @@ -156,6 +158,8 @@ public function getObject() * * @param mixed $entity * + * @throws MappingException + * * @return array */ protected function dehydrate($entity): array @@ -167,7 +171,7 @@ protected function dehydrate($entity): array if (isset($properties[$attributesName])) { $properties[$attributesName] = $this->entityMap->getColumnNamesFromAttributes($properties[$attributesName]); } else { - $properties = $this->entityMap->getColumnNamesFromAttributes($properties); + $properties[$attributesName] = $this->entityMap->getColumnNamesFromAttributes($properties); } $this->properties = $properties; @@ -191,7 +195,7 @@ protected function hydrate() if (isset($properties[$attributesName])) { $properties[$attributesName] = $this->entityMap->getAttributeNamesFromColumns($properties[$attributesName]); } else { - $properties = $this->entityMap->getAttributeNamesFromColumns($properties); + $properties[$attributesName] = $this->entityMap->getAttributeNamesFromColumns($properties); } // In some case, attributes will miss some properties, so we'll just complete the hydration diff --git a/tests/AnalogueTestCase.php b/tests/AnalogueTestCase.php index 86800f0..a40b690 100755 --- a/tests/AnalogueTestCase.php +++ b/tests/AnalogueTestCase.php @@ -274,13 +274,13 @@ protected function rawInsert($table, array $columns) protected function logEvents() { $events = ['store', - 'stored', - 'creating', - 'created', - 'updating', - 'updated', - 'deleting', - 'deleted', + 'stored', + 'creating', + 'created', + 'updating', + 'updated', + 'deleting', + 'deleted', ]; foreach ($events as $event) { diff --git a/tests/DomainTestCase.php b/tests/DomainTestCase.php index f8b1caf..f392617 100755 --- a/tests/DomainTestCase.php +++ b/tests/DomainTestCase.php @@ -16,9 +16,9 @@ public function setUp() } /** - * Insert a User using raw DB query. + * Insert a User using a raw database query. * - * @return User + * @return int The ID of the inserted user */ protected function insertUser() { @@ -35,4 +35,19 @@ protected function insertUser() 'updated_at' => Carbon::now(), ]); } + + /** + * Insert a Group using a raw database query. + * + * @return int The ID of the inserted group + */ + protected function insertGroup() + { + $faker = $this->faker(); + + return $this->rawInsert('groups', [ + 'id' => $this->randId(), + 'name' => $faker->sentence, + ]); + } } diff --git a/tests/MagicTraitsTest.php b/tests/MagicTraitsTest.php index f9ebe41..bfa13c4 100644 --- a/tests/MagicTraitsTest.php +++ b/tests/MagicTraitsTest.php @@ -51,9 +51,9 @@ public function we_can_cast_to_array() $entity = new MagicEntity(); $array = $entity->toArray(); $this->assertEquals([ - 'attr1' => 'Some Value', - 'attr2' => 'Some Value', - ], $array); + 'attr1' => 'Some Value', + 'attr2' => 'Some Value', + ], $array); } /** @test */ @@ -62,8 +62,8 @@ public function we_can_cast_to_json() $entity = new MagicEntity(); $json = $entity->toJson(); $this->assertEquals([ - 'attr1' => 'Some Value', - 'attr2' => 'Some Value', - ], json_decode($json, true)); + 'attr1' => 'Some Value', + 'attr2' => 'Some Value', + ], json_decode($json, true)); } } diff --git a/tests/cases/EntityCollectionTest.php b/tests/cases/EntityCollectionTest.php index 2bc3cfa..95d2d65 100755 --- a/tests/cases/EntityCollectionTest.php +++ b/tests/cases/EntityCollectionTest.php @@ -1,6 +1,5 @@ find($id); $this->assertEquals('123456', $user->rememberToken); $this->assertEquals('adro', $user->identity->fname); + $user->rememberToken = '1234567'; + $mapper->store($user); + $user = null; + $user = $mapper->find($id); + $this->assertEquals('1234567', $user->rememberToken); } /** @test */ @@ -113,6 +118,15 @@ public function we_can_access_camel_case_properities() $movie = null; $movie = $mapper->find($id); $this->assertEquals('analogue is awesome', $movie->getSomeText()); + $movie->setSomeText('analogue is awesome!!'); + $mapper->store($movie); + $movie = null; + $movie2 = $mapper->find($id); + $this->assertEquals('analogue is awesome!!', $movie2->getSomeText()); + $this->seeInDatabase('movies', [ + 'title' => 'Analogue Tutorial', + 'some_text' => 'analogue is awesome!!', + ]); } /** @test */ diff --git a/tests/cases/Relationships/BelongsToManyTest.php b/tests/cases/Relationships/BelongsToManyTest.php index 5ce4cf9..27d60e5 100755 --- a/tests/cases/Relationships/BelongsToManyTest.php +++ b/tests/cases/Relationships/BelongsToManyTest.php @@ -32,8 +32,9 @@ public function we_can_store_a_many_to_many_relationship() } /** @test */ - public function we_can_retrieve_a_many_to_many_relationship() + public function we_can_retrieve_a_one_belongs_to_many_relationship() { + // One user belonging to many groups $userId = $this->createRelatedSet(3); $mapper = $this->mapper(User::class); $user = $mapper->find($userId); @@ -43,6 +44,24 @@ public function we_can_retrieve_a_many_to_many_relationship() } } + /** @test */ + public function we_can_retrieve_a_many_belongs_to_many_relationship() + { + // Many users belonging to many groups + $userIdsToGroupIds = $this->createRelatedSets(3, 3); + + // Test that the relationships we've set up load appropriately + $mapper = $this->mapper(User::class); + foreach ($userIdsToGroupIds as $userId => $groupIds) { + $user = $mapper->find($userId); + $this->assertCount(count($groupIds), $user->groups); + foreach ($user->groups as $group) { + $this->assertContains($group->id, $groupIds); + $this->assertInstanceOf(Group::class, $group); + } + } + } + /** @test */ public function we_can_store_several_entities_in_a_many_to_many_relationship() { @@ -229,7 +248,7 @@ public function related_items_are_not_created_twice_when_storing_twice() } /** @test */ - public function belongs_to_many_relationship_can_be_eager_loaded() + public function one_belongs_to_many_relationship_can_be_eager_loaded() { $database = $this->app->make('db'); $userId = $this->createRelatedSet(1); @@ -241,6 +260,29 @@ public function belongs_to_many_relationship_can_be_eager_loaded() $this->assertNotInstanceOf(ProxyInterface::class, $user->groups); } + /** @test */ + public function many_belongs_to_many_relationship_can_be_eager_loaded() + { + // Create many users belonging to many groups + $userIdsToGroupIds = $this->createRelatedSets(2, 2); + + // Test that the relationships we've set up are eager loaded appropriately + $mapper = $this->mapper(User::class); + + $users = $mapper->with('groups')->get(); + + foreach ($users as $user) { + $groupIds = $userIdsToGroupIds[$user->id]; + $this->assertNotInstanceOf(ProxyInterface::class, $user->groups); + $this->assertInstanceOf(EntityCollection::class, $user->groups); + $this->assertCount(count($groupIds), $user->groups); + foreach ($user->groups as $group) { + $this->assertContains($group->id, $groupIds); + $this->assertInstanceOf(Group::class, $group); + } + } + } + /** @test */ public function empty_eagerloaded_belongs_to_many_relationship_is_an_empty_entity_collection() { @@ -299,6 +341,8 @@ public function we_can_use_a_many_to_many_relationship_on_entities_with_custom_p /** * Create a random related set. * + * @param int $relatedCount + * * @return int */ protected function createRelatedSet($relatedCount = 1) @@ -317,4 +361,51 @@ protected function createRelatedSet($relatedCount = 1) return $userId; } + + /** + * Create two randomly related sets. + * + * @param int $rootCount [optional] The number of root entities to create. Defaults to 3. + * @param int $relatedCount [optional] The number of related entities to create. Defaults to 3. + * + * @return array [userId => [groupId, groupId, ...], ...] A map of root entity IDs as keys to a list of related entity IDs as values + */ + protected function createRelatedSets($rootCount = 3, $relatedCount = 3) + { + $userIds = []; + $groupIds = []; + $userIdsToGroupIds = []; + + // Create users + for ($i = 0; $i < $rootCount; $i++) { + $userIds[] = $this->insertUser(); + } + + // Create groups + for ($i = 0; $i < $relatedCount; $i++) { + $groupIds[] = $this->insertGroup(); + } + + // Relate each user to every group + for ($i = 0; $i < $rootCount; $i++) { + $userId = $userIds[$i]; + + for ($j = 0; $j < $relatedCount; $j++) { + $groupId = $groupIds[$j]; + + $this->rawInsert('groups_users', [ + 'user_id' => $userId, + 'group_id' => $groupId, + ]); + + if (empty($userIdsToGroupIds[$userId])) { + $userIdsToGroupIds[$userId] = []; + } + + $userIdsToGroupIds[$userId][] = $groupId; + } + } + + return $userIdsToGroupIds; + } } diff --git a/tests/src/Maps/MovieMap.php b/tests/src/Maps/MovieMap.php index 8157f9b..fce3db4 100755 --- a/tests/src/Maps/MovieMap.php +++ b/tests/src/Maps/MovieMap.php @@ -12,12 +12,11 @@ class MovieMap extends EntityMap 'id', 'title', 'realisator', + 'some_text', ]; protected $camelCaseHydratation = true; - protected $arrayName = null; - public function realisator(Movie $movie) { return $this->belongsTo($movie, Realisator::class);