Skip to content

Commit

Permalink
#17 Continued implementing support for many-to-many relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
hexus committed Feb 16, 2021
1 parent ab85c20 commit 0d194a1
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/Darya/ORM/EntityFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
interface EntityFactory
{
/**
* Create an entity with the given properties.
* Create an entity with the given attributes.
*
* @param array $attributes [optional] The attributes to create the entity with.
* @return mixed
Expand Down
21 changes: 9 additions & 12 deletions src/Darya/ORM/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Darya\ORM;

use Darya\ORM\Exception\EntityNotFoundException;
use Darya\ORM\Exception\MappingException;
use Darya\Storage;
use Darya\Storage\Queryable;
use RuntimeException;
Expand Down Expand Up @@ -126,7 +127,7 @@ public function graph(): EntityGraph
*
* @param string $entity The entity name.
* @param string|null $storage The storage to use.
* @return Mapper
* @return Mapper The entity's mapper.
*/
public function mapper(string $entity, string $storage = null): Mapper
{
Expand All @@ -135,25 +136,21 @@ public function mapper(string $entity, string $storage = null): Mapper
}

if (!isset($this->storages[$storage])) {
// TODO: MappingException
throw new UnexpectedValueException("Unknown storage '$storage'");
throw MappingException::unknownStorage($storage);
}

$storage = $this->storages[$storage];
$storage = $this->storages[$storage];
$entityMap = $this->graph->getEntityMap($entity);

return new Mapper(
$this,
$this->graph->getEntityMap($entity),
$storage
);
return new Mapper($this, $entityMap, $storage);
}

/**
* Find an entity with the given ID.
*
* @param string $entity The entity to find.
* @param mixed $id The ID of the entity to find.
* @return object|null
* @return object|null The entity, if found. `null` otherwise.
*/
public function find(string $entity, $id)
{
Expand All @@ -165,7 +162,7 @@ public function find(string $entity, $id)
*
* @param string $entity The entity to find.
* @param mixed $id The ID of the entity to find.
* @return object The entity.
* @return object The entity, found or created.
*/
public function findOrNew(string $entity, $id)
{
Expand Down Expand Up @@ -193,7 +190,7 @@ public function findOrFail(string $entity, $id)
*
* @param string $entity The entity to find.
* @param mixed[] $id The ID of the entity to find.
* @return object|null The entities.
* @return object[] The entities found.
*/
public function findMany(string $entity, array $id)
{
Expand Down
1 change: 1 addition & 0 deletions src/Darya/ORM/EntityMapFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public function create(string $name, array $mapping, string $resource = null): E
$resource = $name;
}

// TODO: Model is an abstract class, we need something concrete and final
$entityMap = new EntityMap(
Model::class, $resource, $mapping, new PropertyStrategy()
);
Expand Down
10 changes: 6 additions & 4 deletions src/Darya/ORM/Exception/EntityNotFoundException.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
*
* Thrown when a requested entity does not exist in storage.
*
* TODO: Enforce the entity name via the constructor and provide a convenient factory method
*
* @author Chris Andrew <[email protected]>
*/
class EntityNotFoundException extends RuntimeException
Expand All @@ -17,16 +19,16 @@ class EntityNotFoundException extends RuntimeException
*
* @var string
*/
protected $entity;
protected string $entity;

/**
* Get the name of the entity.
*
* @return string
*/
public function getEntityName()
public function getEntityName(): string
{
return $this->entity;
return $this->entity ?? '';
}

/**
Expand All @@ -35,7 +37,7 @@ public function getEntityName()
* @param string $entity
* @return $this
*/
public function setEntityName($entity)
public function setEntityName(string $entity)
{
$this->entity = $entity;

Expand Down
20 changes: 20 additions & 0 deletions src/Darya/ORM/Exception/MappingException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Darya\ORM\Exception;

use RuntimeException;
use Throwable;

/**
* Darya's mapping exception.
*
* Thrown for entity mapping related errors.
*
* @author Chris Andrew <[email protected]>
*/
class MappingException extends RuntimeException
{
public static function unknownStorage(string $storage, Throwable $previous = null): self {
return new static("Unknown storage '$storage'", null, $previous);
}
}
5 changes: 3 additions & 2 deletions src/Darya/ORM/Relationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ public function __construct(string $name, EntityMap $parentMap, EntityMap $relat
/**
* Build an instance of this relationship query for a given parent entity.
*
* @param mixed $entity The parent entity.
* @param mixed $entity The parent entity.
* @param EntityManager $orm
* @return Relationship The new relationship query.
*/
abstract public function forParent($entity): Relationship;
abstract public function forParent($entity, EntityManager $orm): Relationship;

/**
* Build an eager-loading instance of this relationship query for the given parent entities.
Expand Down
2 changes: 1 addition & 1 deletion src/Darya/ORM/Relationship/BelongsTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
class BelongsTo extends Relationship
{
public function forParent($entity): Relationship
public function forParent($entity, EntityManager $orm): Relationship
{
$query = clone $this;

Expand Down
36 changes: 21 additions & 15 deletions src/Darya/ORM/Relationship/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,47 +19,51 @@ class BelongsToMany extends Relationship
protected string $parentForeignKey;

/**
* The resource that contains relationships between the parent and related entities.
* The entity that represents relationships between the parent and related entities.
*
* Represents the equivalent of a junction table in SQL.
* Represents the equivalent of a junction table in SQL; one that contains foreign key tuples.
*/
protected string $junctionResource;
protected string $associativeEntity;

public function __construct(
string $name,
EntityMap $parentMap,
EntityMap $relatedMap,
string $foreignKey,
string $parentForeignKey,
string $junctionResource
string $associativeEntity
) {
parent::__construct($name, $parentMap, $relatedMap, $foreignKey);

$this->parentForeignKey = $parentForeignKey;
$this->junctionResource = $junctionResource;
$this->parentForeignKey = $parentForeignKey;
$this->associativeEntity = $associativeEntity;
}

public function forParent($entity): Relationship
public function forParent($entity, EntityManager $orm): Relationship
{
// TODO: Implement forParent() method.
$query = clone $this;

$parentId = $this->getParentId($entity);
$relatedIdsQuery = $orm->query($this->associativeEntity)
->fields([$this->foreignKey])
->where($this->parentForeignKey, $parentId);

$relatedKey = $this->getRelatedKey();
$query->where("$relatedKey in", $relatedIdsQuery);

return $query;
}

public function forParents(array $entities, EntityManager $orm): Relationship
{
$query = clone $this;

// TODO: Be smarter than using default storage for junction queries
// Consider automapping junction entities where they're not, or forcing them to be provided, etc.
$parentIds = $this->getParentIds($entities);
$relatedIdsQuery = $orm->getDefaultStorage()
->query($this->junctionResource)
$relatedIdsQuery = $orm->query($this->associativeEntity)
->fields([$this->foreignKey])
->where("{$this->parentForeignKey} in", $parentIds);

$relatedKey = $this->getRelatedMap()->getStorageKey();
$relatedKey = $this->getRelatedKey();
$query->where("$relatedKey in", $relatedIdsQuery);

return $query;
Expand All @@ -68,11 +72,13 @@ public function forParents(array $entities, EntityManager $orm): Relationship
public function match(array $parentEntities, array $relatedEntities): array
{
// TODO: Implement match() method.
// Find a sensible way to share junction entities with this method
// Load associative entities into memory explicitly and build a dictionary
// for matching
// @see \Darya\ORM\Relation\BelongsToMany::eager()
$parentIds = $this->getParentIds($parentEntities);
$relatedIds = $this->getRelatedIds($relatedEntities);

//var_dump($parentIds, $relatedIds);
var_dump($parentIds, $relatedIds);
//die;

return $parentEntities;
Expand Down
5 changes: 3 additions & 2 deletions src/Darya/ORM/Relationship/Has.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Darya\ORM\Relationship;

use Darya\ORM\EntityManager;
use Darya\ORM\Relationship;

/**
Expand All @@ -11,7 +12,7 @@
*/
class Has extends Relationship
{
public function forParent($entity): Relationship
public function forParent($entity, EntityManager $orm): Relationship
{
$query = clone $this;

Expand All @@ -23,7 +24,7 @@ public function forParent($entity): Relationship
return $query;
}

public function forParents(array $entities, \Darya\ORM\EntityManager $orm): Relationship
public function forParents(array $entities, EntityManager $orm): Relationship
{
$query = clone $this;

Expand Down
14 changes: 12 additions & 2 deletions tests/Functional/ORM/EntityManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,27 @@ public function setUp()
'posts'
);

$userRoleMap = $this->factory->create(
'UserRole',
[
'user_id' => 'user_id',
'role_id' => 'role_id'
],
'users_roles'
);

$this->graph = new EntityGraph(
[
$userMap,
$postMap,
$roleMap
$roleMap,
$userRoleMap
],
[
new BelongsTo('master', $userMap, $userMap, 'master_id'),
new Has('padawan', $userMap, $userMap, 'master_id'),
new HasMany('posts', $userMap, $postMap, 'author_id'),
new BelongsToMany('roles', $userMap, $roleMap, 'role_id', 'user_id', 'users_roles')
new BelongsToMany('roles', $userMap, $roleMap, 'role_id', 'user_id', 'UserRole')
]
);
}
Expand Down

0 comments on commit 0d194a1

Please sign in to comment.