diff --git a/README.md b/README.md index 01da0b8..bdd5419 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,99 @@ Spiral ORM [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/spiral/orm/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/spiral/orm/?branch=master) [![Coverage Status](https://coveralls.io/repos/github/spiral/orm/badge.svg?branch=master)](https://coveralls.io/github/spiral/orm?branch=master) -[Documentation](http://spiral-framework.com/guide) | [CHANGELOG](/CHANGELOG.md) \ No newline at end of file +ORM engine with automatic database scaffolding, strict schemas, code discovery, modular database partitions and various relation loaders. Hackable! + +[Full Documentation](http://spiral-framework.com/guide) | [CHANGELOG](/CHANGELOG.md) + +# Documentation + * [Overview](https://spiral-framework.com/guide/orm/overview.md) + * [Record and RecordEntity](https://spiral-framework.com/guide/orm/entities.md) + * [Repositories and Selectors](https://spiral-framework.com/guide/orm/repositories.md) + * [Accessors and Filters](https://spiral-framework.com/guide/orm/accessors.md) + * [Column Objects](https://spiral-framework.com/guide/orm/columns.md) + * [Scaffolding and Migrations](https://spiral-framework.com/guide/orm/scaffolding.md) + * [Transactions](https://spiral-framework.com/guide/orm/transactions.md) + * [Relations](https://spiral-framework.com/guide/orm/relations.md) + * [Morphed Relations](https://spiral-framework.com/guide/orm/morphed-relations.md) + * [Pre-compiled Relations](https://spiral-framework.com/guide/orm/late-binding.md) + * [Query Models](https://spiral-framework.com/guide/orm/query.md) + * [Eager loading](https://spiral-framework.com/guide/orm/loading.md) + * [Recursive Relations](https://spiral-framework.com/guide/orm/recursive-relations.md) + * [Hybrid Databases](https://spiral-framework.com/guide/orm/odm-bridge.md) + * [Custom Relations](https://spiral-framework.com/guide/orm/custom-relations.md) + +# Examples + +```php +class Post extends RecordEntity +{ + use TimestampsTrait; + + //Database partitions, isolation and aliasing + const DATABASE = 'blog'; + + const SCHEMA = [ + 'id' => 'bigPrimary', + 'title' => 'string(64)', + 'status' => 'enum(published,draft)', + 'body' => 'text', + + //Simple relation definitions + 'comments' => [self::HAS_MANY => Comment::class], + + //Not very simple relation definitions + 'collaborators' => [ + self::MANY_TO_MANY => User::class, + self::PIVOT_TABLE => 'post_collaborators_map', + self::PIVOT_COLUMNS => [ + 'time_assigned' => 'datetime', + 'type' => 'string, nullable', + ], + User::INVERSE => 'collaborated_posts' + ], + + //Pre-compiled relations + 'author' => [ + self::BELONGS_TO => AuthorInterface::class, + self::LATE_BINDING => true + ], + + //Hybrid databases + 'metadata' => [ + Document::ONE => Mongo\Metadata::class + ] + ]; +} +``` + +```php +$posts = $postSource->find()->distinct() + ->with('comments', ['where' => ['{@}.approved' => true]]) //Automatic joins + ->with('author')->where('author_name', 'LIKE', $authorName) //Fluent + ->load('comments.author') //Cascade eager-loading (joins or external query) + ->paginate(10) //Quick pagination using active request + ->getIterator(); + +foreach ($posts as $post) { + echo $post->author->getName(); +} +``` + +```php +$post = new Post(); +$post->publish_at = 'tomorrow 8am'; +$post->author = new User(['name' => 'Antony']); + +$post->tags->link(new Tag(['name' => 'tag A'])); +$post->tags->link($tags->findOne(['name' => 'tag B'])); + +$transaction = new Transaction(); +$transaction->store($post); +$transaction->run(); + +//--or--: Active record (optional) +$post->save(); + +//--or--: request specific transaction +$this->transaction->store($post); +``` diff --git a/source/Spiral/ORM/Entities/Loaders/AbstractLoader.php b/source/Spiral/ORM/Entities/Loaders/AbstractLoader.php index 50631ab..c99507f 100644 --- a/source/Spiral/ORM/Entities/Loaders/AbstractLoader.php +++ b/source/Spiral/ORM/Entities/Loaders/AbstractLoader.php @@ -184,8 +184,10 @@ final public function loadRelation( } if ($join) { - //Let's tell our loaded that it's method is JOIN (forced) - $options['method'] = self::JOIN; + if (empty($options['method']) || !in_array($options['method'], [self::JOIN, self::LEFT_JOIN])) { + //Let's tell our loaded that it's method is JOIN (forced) + $options['method'] = self::JOIN; + } } if (isset($loaders[$relation])) { @@ -364,4 +366,4 @@ private function loadChain(string $chain, array $options, bool $join): LoaderInt $join ); } -} \ No newline at end of file +} diff --git a/source/Spiral/ORM/Entities/Relations/Traits/SyncedTrait.php b/source/Spiral/ORM/Entities/Relations/Traits/SyncedTrait.php index 87b2ef1..5f8b492 100644 --- a/source/Spiral/ORM/Entities/Relations/Traits/SyncedTrait.php +++ b/source/Spiral/ORM/Entities/Relations/Traits/SyncedTrait.php @@ -25,7 +25,7 @@ trait SyncedTrait */ protected function isSynced(RecordInterface $inner, RecordInterface $outer): bool { - if (empty($outer->primaryKey())) { + if (empty($inner->primaryKey()) || empty($outer->primaryKey())) { //Parent not saved return false; } @@ -46,4 +46,4 @@ protected function isSynced(RecordInterface $inner, RecordInterface $outer): boo * @return string|null */ abstract protected function key(int $key); -} \ No newline at end of file +} diff --git a/source/Spiral/ORM/Schemas/Relations/BelongsToMorphedSchema.php b/source/Spiral/ORM/Schemas/Relations/BelongsToMorphedSchema.php index 7df410e..a4f2f80 100644 --- a/source/Spiral/ORM/Schemas/Relations/BelongsToMorphedSchema.php +++ b/source/Spiral/ORM/Schemas/Relations/BelongsToMorphedSchema.php @@ -152,7 +152,11 @@ public function declareTables(SchemaBuilder $builder): array $outerKey = $this->findOuter($builder); if (empty($outerKey)) { - throw new RelationSchemaException("Unable to build morphed relation, no outer record found"); + throw new RelationSchemaException(sprintf( + "Unable to build morphed relation, no outer record(s) found for '%s' from '%s'", + $this->getDefinition()->getTarget(), + $this->getDefinition()->sourceContext()->getClass() + )); } //Make sure all tables has same outer @@ -198,4 +202,4 @@ public function packRelation(SchemaBuilder $builder): array return $packed; } -} \ No newline at end of file +}