GithubHelp home page GithubHelp logo

analogue's Introduction

(this project is looking for a new maintainer)

Analogue ORM

Latest Stable Version Latest Unstable Version License Build Status StyleCI

Analogue is a flexible, easy-to-use ORM for PHP. It is a transposition of the Eloquent ORM that ships with Laravel framework using a Data Mapper pattern instead of the original Active Record approach. it overcomes some of Eloquent's architectural limitations by using a strict separation of concerns; for example, you can use Value Objects or Single-table-inheritance, which are hard/impossible to implement correctly using the native ORM.

As a Laravel package, it integrates flawlessly inside the framework, and provides a more powerfull peristance layer, allowing to build enterprise-grade applications while retaining a simple and enjoyable development experience.

Installation

composer require analogue/orm

See Configuration for more information.

Concept

The concept is simple; your model layer is defined using 2 classes : one Entity, which can be any PHP class or extends the base Analogue\ORM\Entity class which provides magic getters and setters, and one EntityMap which defines relationships, castings, table name, database column names.

Take this simple domain model :

use Analogue\ORM\Entity;
use Illuminate\Support\Collection;

class Blog extends Entity
{
    public function __construct()
    {
        $this->posts = new Collection;
    }

    public function addPost(Post $post)
    {
        $this->posts->push($post);
    }
}

class Post extends Entity
{
 
}

We can instruct Analogue how these objects are related using these classes :

use Analogue\ORM\EntityMap;

class BlogMap extends EntityMap
{
    public function posts(Blog $blog)
    {
        return $this->hasMany($blog, Post::class);
    }
}

class PostMap extends EntityMap
{
    public function blog(Post $post)
    {
        return $this->belongsTo($post, Blog::class);
    }
}

Now we can create related instance of or object and persist them to the database :

$blog = new Blog;
$blog->title = "My first blog";

$post = new Post; 
$post->title->"My first post";

$blog->addPost($post);

// Only the blog instance need to explicitely stored; Analogue takes care of synchronizing
// related objects behinds the scene. 

mapper(Blog::class)->store($blog);

Once our objects are persisted into the database, we can query them using the fluent query builder :

$blog = mapper(Blog::class)->first();

echo $blog->posts->first()->title; // 'My first post'

Documentation

Check the Documentation for more details.

Features

  • Framework agnostic
  • Lazy loading
  • Eager Loading
  • Timestamps
  • Soft Deletes
  • Value Objects
  • Polymorphic Relationships
  • Dynamic Relationships
  • Single table inheritance
  • Cast entities to Array / Json
  • Flexible event system
  • Native multiple database connections support
  • Extendable via custom database drivers / plugins

Changelog

Version 5.6

  • Laravel 5.6 support
  • Bring back ability to map DB columns that name are not equals to the name of the attribute.
  • Add ability to map DB snake case columns to camel case properties on entities.

Version 5.5

  • Laravel 5.5 support
  • Pushed miminum requirements to PHP7
  • Complete support of Plain PHP objects via reflection based hydration/dehydration
  • Improved Lazy-loading proxies.
  • New, more flexible Value Object implementation, that can now be defined as embedsOne(), embedsMany() relationships
  • Embedded value object can now be stored as a mysql JSON field
  • Analogue entities can now be instantiated using laravel's IoC Container or any PSR-11 compatible container.
  • Added MongoDB driver.
  • Package auto discovery (L5.5)

Version 5.4

  • Illuminate 5.4 Compatibility.
  • Add Ability to map DB columns that name are not equals to the name of the attribute.

Version 5.3

  • Illuminate 5.3 Compatibility.
  • Now fully support Single Table Inheritance.

Version 5.1

  • Illuminate 5.1 + 5.2 Compatibility.

Version 5.0

  • Analogue version now mirrors illuminate version.

Version 2.1.3

  • Mutator feature in base Entity class.
  • Ability to add entities to a proxy collection without lazyloading it.

Version 2.1

  • Package is now framework agnostic.
  • Now support any plain object that implements Mappable interface.
  • Introducing a MappableTrait for quick implementation.
  • Queries can now be run directly on the mapper Object.
  • Store/Delete methods now accept a array and collections as argument.
  • EntityMap are now autodected when in the same namespace as the entity.
  • Base Entity class Supports hidden attributes.
  • Many workflow related improvements.

Version 2.0

  • Laravel 5 Support.

Documentation

Check the wiki for full documentation.

Licence

This package is licensed under the MIT License.

analogue's People

Contributors

adrorocker avatar bryant1410 avatar cappuc avatar clarodeus avatar dylan-dpc avatar evertt avatar gdsmith avatar graemetait avatar hexus avatar kalvisbuls avatar kieranajp avatar lex111 avatar maxmirazh33 avatar otzy avatar quetzyg avatar remicollin avatar rickycheers avatar scrutinizer-auto-fixer avatar serafimarts avatar shin1x1 avatar smallhadroncollider avatar tabennett avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

analogue's Issues

Composite primary key

Honestly I haven't actually tested this, but I'm guessing Analogue wasn't made to work with composite primary keys, is that correct?

If that's true, would you like to make it? Or like it if I make it? Because I'd really like this feature. :-)

Bug or not correctly wiring?

Don't know exactly where to ask for help in this situation, I think docs should be more comprehensive, I mean, to show better how to wire things up with usual current MVC implementantions, or show what pieces are axactly to be replaced and how. Would help us to make the "mental shift" from pure MVC to correct DDD), I'm still grasping the entity and value objects concepts, and the implementation is getting off of my hands, i'm not sure if not connecting things right or just library bug, for example from:

namespace App\Repositories\Todo;

use Analogue\ORM\Repository;
use App\Entities\Todo;

class TodoRepository extends Repository{    //compacted for brevity
    public function __construct(){
        parent::__construct(Todo::class);
    }
    ...
}

I got

FatalErrorException in Manager.php line 128:
Call to a member function mapper() on null

1. in Manager.php line 128
2. at FatalErrorException->__construct() in HandleExceptions.php line 131
3. at HandleExceptions->fatalExceptionFromError() in HandleExceptions.php line 116
4. at HandleExceptions->handleShutdown() in HandleExceptions.php line 0
5. at Manager::getMapper() in Repository.php line 35
6. at Repository->__construct() in TodoRepository.php line 21

Any advise, or bug fix from here?

Illuminate 5.1 packages

Is it possible to use Laravel/Lumen 5.1 LTS i.e. the Illuminate 5.1 packages? For now 5.0.* is defined as the only compatible version in composer.json.

5.1.7: CollectionProxy->count() Error

Only seems to affect 5.1.7.

When I run $someCollection->count() I get the following error:

ErrorException in CollectionProxy.php line 94: Invalid argument supplied for foreach()

at CollectionProxy->loadOnce() in CollectionProxy.php line 109

Hidden attributes

Would be nice to hide attributes (similar to what Eloquent does) by setting a $hidden attribute on the EntityMap, which prevents certain methods to provide those hidden attributes.

Multiple connections issue or better to say feature request

Hi guys! I really like this ORM. I have one question... lets say Entity User is at server connection mysql_users and entity Article is on server connection mysql_articles and we make pivot table somewhere on one of those two servers or even 3rd server. Why am I telling you this, some applications really have to scale horizontally by adding servers for different services.

In this case there is no way to do left join, right join... and similar but basically it would be necessary to do separate queries to get id's from pivot and then data from the other entity similar to eager loading. Can it be done trough ORM instead of doing it manually when we want to do hasMany or belongsToMany.

ErrorException in EntityCollection.php line 9:

Declaration of Analogue\ORM\EntityCollection::max() should be compatible with Illuminate\Support\Collection::max($key = NULL)

I am receiving this error when trying to complete a find(11).

Any ideas? Not sure if I am doing something wrong!

Carbon object in Entity::toArray()

Hello.

I'd just like to sound this out, and see if this is expected behaviour.

I have the following piece of code to find an authenticated user:-

        $user = $this->mapper
            ->where('email', $command->getEmail())
            ->first()
        ;

        if ( ! $user) {
            return false;
        }

        if ( ! password_verify($command->getPassword(), $user->password)) {
            return false;
        }

        if (password_needs_rehash($user->password, PASSWORD_DEFAULT)) {
            $user->password = password_hash($command->getPassword(), PASSWORD_DEFAULT);
            $this->mapper->store($user);
        }

        return $user->toArray();

If seems that when the password needs rehashing, and I subsequently update the user, I'm getting an instance of Carbon returned within the toArray call for the updated_at property. This means I get mixed results from this method, which of course isn't ideal.

I suspect it's related to the user table having aCURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.

Is this something you would like me to look at? I assume it should just return an updated string, but I wanted to the implications of changing this behaviour.

Thanks!

Updating an existing entity with a relationship property doesn't update the foreign key

I have a Document entity which links to a File using a belongsTo relationship:

    public function file(Document $document)
    {
        return $this->belongsTo($document, File::class, "file_id", "id");
    }

If I take an existing document and then set file to a new File entity, when I store the Document the file_id key does not get updated.


The Document just before running store on it. Afterwards the file_id remains 575, not 581.

Document {#626
  #attributes: array:17 [
    "id" => "487"
    "title" => "Aliquid proident rerum sint ratione ducimus quisquam reprehenderit veniam ad"
    "file_id" => "575"
    "file" => File {#636
      #attributes: array:8 [
        "location" => "documents"
        "id" => 581
        "filename" => "kYQVgWog.pdf"
      ]
    }
  ]
}

Entities and cache

Hi, I'm trying to use this in a project and all worked fine until I dropped the table I was working with from the database in order to change some columns. Now I get this error:
Fatal error: Uncaught exception 'Analogue\ORM\Exceptions\MappingException' with message 'Entity primary key contradicts with cached Entity. You probably modified it by mistake' in /Server/redbooth/vendor/analogue/orm/src/System/StateChecker.php:81 Stack trace: #0 /Server/redbooth/vendor/analogue/orm/src/Commands/Store.php(81): Analogue\ORM\System\StateChecker->exists() #1 /Server/redbooth/vendor/analogue/orm/src/System/Mapper.php(141): Analogue\ORM\Commands\Store->execute() #2 /Server/redbooth/vendor/analogue/orm/src/System/Mapper.php(104): Analogue\ORM\System\Mapper->storeEntity(Object(Project)) #3 /Server/redbooth/index.php(16): Analogue\ORM\System\Mapper->store(Object(Project)) #4 {main} thrown in /Server/redbooth/vendor/analogue/orm/src/System/StateChecker.php on line 81
It mentions an Entity cache, but I can't seem to flush that because I can't find where it's supposed to store the Entity cache.

This is my model:

<?php
use Analogue\ORM\Entity;

class Project extends Entity
{
    public function __construct($id, $name, $created_at, $updated_at)
    {
        $this->id = $id;
        $this->name = $name;
        $this->created_at = $created_at;
        $this->updated_at = $updated_at;
    }
}
?>

projects.id is the primary key but not auto-incrementing because it shouldn't.

How do I drop or flush the entity cache?

Nested Where Queries: newQueryWithoutScopes() method not found

Trying to do a nested query e.g.

$this->mapper->where("disabled", false)
             ->where(function ($query) use ($location) {
                 $query->orWhere("city", "=", $location);
                 $query->orWhere("country", "=", $location);
             })->get();

Get a BadMethodCallException: Call to undefined method Illuminate\Database\Query\Builder::newQueryWithoutScopes()

Laravel 5.1.24

There appear to be a few new method signatures for Collections in Laravel 5.1.24, so Analogue stops working when I try to upgrade from 5.1.20.

The prepend method was the specific error I got, but there may be others.

Generation of primary key value (e.g. using UUID)

I'm looking for a way to create the primary key value when creating a new entity. Something like this:

$entity = new MyEntity;
print $entity->id; // "550e8400-e29b-11d4-a716-446655440000"

It would be nice to have a primary key generated before the entity gets saved to the database.
At present if I modify the id property by hand the StateChecker complains about it.

What's the best way to achieve this? Thanks!

make everything conform to PSR-2

I'm starting to get a bit annoyed by the mixture of tabs-indentation and spaces-indentation in the code.
After we finish up the last pull-requests I'll do it myself. :-)

Getting relation of unstored entity throws error

Doing something like:

$authorization = new Authorization;
$authorization->client_id = '6dfd197a-dc34-4f3f-873b-0c48b3ef2027';
print $authorization->client->id;

throws an error:

ErrorException in AuthorizationController.php line 33: Trying to get property of non-object

Doing this, on the other hand works:

$authorization = new Authorization;
$authorization->client_id = '6dfd197a-dc34-4f3f-873b-0c48b3ef2027';
$authorizationRepository->store($authorization);
print $authorization->client->id;

Is it really needed to store an entity before being able to get it's related (existing) objects?

Dynamic Relationships Usage

The EntityMap provides methods for setting dynamic relationships and I was wondering how the Entity itself copes with those dynamic relationships. E.g. in the documentation example the Entity defines a hard-coded method addComment(Comment $comment).

Wouldn't it be neccessary to be able to define dynamic getters and setters on the Entity itself to be able to enjoy the benefits of dynamic relationships for the EntityMap?

Maybe I didn't understand the concept.

Or would one have to implement dynamic getters/setters for dynamically added relationships on their own? Would be awesome if you could provide a small example if you have time! :)

Interface `Authenticatable` needs `getAuthIdentifier` on the Entity

Somewhere Laravel calls the method getAuthIdentifier on the Entity and for now I use this:

class User extends Entity
    public function getAuthIdentifier() {
        return $this->id;
    }
}

I noticed that the primary key is defined in the EntityMap.

What's the correct way to get the field name of the primary key inside the Entity?

When I delete a BelongsToMany relationship and then persist the entity then the deletion is not persisted.

So I have a model User which BelongsToMany Permission. I've written a trait that does all the permission management, which looks like this:

namespace App\Model\Traits;

use App\Model\Entities\Permission;
use App\Model\Repositories\PermissionRepository;

trait Permissions
{
    public function hasPermissionTo($action, $ownership = null, $entity = null)
    {
        $permission = $this->getStoredPermission($action, $ownership, $entity);

        return $this->permissions->contains($permission->id);
    }

    public function givePermissionTo($action, $ownership = null, $entity = null)
    {
        $permission = $this->getStoredPermission($action, $ownership, $entity);

        $this->attributes['permissions']->add($permission);

        return $this;
    }

    public function revokePermissionTo($action, $ownership = null, $entity = null)
    {
        $permission = $this->getStoredPermission($action, $ownership, $entity);

        $this->attributes['permissions']->remove($permission);

        return $this;
    }

    protected function getStoredPermission($action, $ownership, $entity)
    {
        if ($action instanceof Permission) return $action;

        if (is_object($entity)) $entity = get_class($entity);

        $repository = app(PermissionRepository::class);

        $ownership = [$ownership, 'any'];

        $conditions = compact('action', 'ownership', 'entity');

        $permission = $repository->firstMatching($conditions);

        if ($permission) return $permission;

        return new Permission($action, $ownership[0], $entity);
    }
}

And I use this trait in my User entity. Now take my word for it that the getStoredPermission() method works perfectly. Also, I've tested givePermissionTo() with this code:

$user = $userRepository->find(1);
$user->givePermissionTo('edit', 'other', $user);
mapper($user)->store($user);

When I run that code, a permission is created and is also correctly linked to the user. So I was hoping that doing the following would delete the link:

$user = $userRepository->find(1);
$user->revokePermissionTo('edit', 'other', $user);
mapper($user)->store($user);

Now I can see that the code does correctly remove the permission from the EntityCollection in $user->attributes['permissions'], but somehow when I store the user, it doesn't remove the link in the database.

updating an entity

to illustrate the problem i have two tests...

     /**
     * @test
     */
    public function it_creates_a_user()
    {
        $userMapper = \Analogue::mapper(User::class);
        $user = new User(new Identity('first name', 'last name'));
        $userMapper->store($user);
        $user = $userMapper->find(1);
        $userMapper->store($user);
    }

    /**
     * @test
     */
    public function it_updates_a_user()
    {
        $userMapper = \Analogue::mapper(User::class);
        $user = $userMapper->find(1);
        $userMapper->store($user);
    }

first one is ok, second one (record with id 1 exists in database ) fails with the message:

Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 no such column: first_name (SQL: update "users" set "first_name" = first name, "last_name" = last name where "id" = 1)

Add support for tables without primary key

Sometimes in case of handling data in second normal form there comes tables with no need for primary key. But ORM requires primary key to manipulate entities.

My proposal is to add compound keys for such cases.

For example $primaryKey may look like this in EntityMap ['good_id', 'bill_id'] and concatenation in subsequent use.

This will grant unique identifier for each entity in table without primary key.

Carbon::toString() fails when updating an entity

I get the error Call to undefined method Carbon\Carbon::toString() in /vendor/analogue/orm/src/ValueObject.php on line 153 when updating an entity. Carbon has a method __toString() but I can't find toString().

Any ideas?

Failing to insert into pivot table in Many to Many relationship

Hi, thanks for your great contribution!

I found that the example for your Many to Many relationship does not work properly (https://github.com/analogueorm/analogue/wiki/Configuring-Entity-Maps#many-to-many). I tried to attach a resource to a user and it worked fine, then I tried to attach another resource to the same User and it raises an exception:
ErrorException in helpers.php line 689: Object of class Analogue\ORM\Relationships\Pivot could not be converted to string

Here is the error log:

in helpers.php line 689
at HandleExceptions->handleError('4096', 'Object of class Analogue\ORM\Relationships\Pivot could not be converted to string', '/vagrant/public/mercury/vendor/laravel/framework/src/Illuminate/Support/helpers.php', '689', array('search' => '\?', 'replace' => array(object(Pivot), '1'), 'subject' => 'update `resources` set `pivot` = ? where `id` = ?', 'value' => object(Pivot)))
at preg_replace('/\?/', 'Object', 'update `resources` set `pivot` = ? where `id` = ?', '1') in helpers.php line 689
at str_replace_array('\?', array(object(Pivot), '1'), 'update `resources` set `pivot` = ? where `id` = ?') in QueryException.php line 55
at QueryException->formatMessage('update `resources` set `pivot` = ? where `id` = ?', array(object(Pivot), '1'), object(PDOException)) in QueryException.php line 37
at QueryException->__construct('update `resources` set `pivot` = ? where `id` = ?', array(object(Pivot), '1'), object(PDOException)) in Connection.php line 622
at Connection->runQueryCallback('update `resources` set `pivot` = ? where `id` = ?', array(object(Pivot), '1'), object(Closure)) in Connection.php line 576
at Connection->run('update `resources` set `pivot` = ? where `id` = ?', array(object(Pivot), '1'), object(Closure)) in Connection.php line 383
at Connection->affectingStatement('update `resources` set `pivot` = ? where `id` = ?', array(object(Pivot), '1')) in Connection.php line 328
at Connection->update('update `resources` set `pivot` = ? where `id` = ?', array(object(Pivot), '1')) in Builder.php line 1733
at Builder->update(array('pivot' => object(Pivot))) in Store.php line 718
at Store->update() in Store.php line 98
at Store->execute() in Mapper.php line 131
at Mapper->storeEntity(object(Resource)) in Mapper.php line 106
at Mapper->store(object(Resource)) in Store.php line 623
at Store->updateEntityIfDirty(object(Resource)) in Store.php line 594
at Store->updateDirtyRelated() in Store.php line 420
at Store->postStoreProcess() in Store.php line 103
at Store->execute() in Mapper.php line 131
at Mapper->storeEntity(object(User)) in Mapper.php line 106
at Mapper->store(object(User)) in WelcomeController.php line 69
at WelcomeController->finalTest()
at call_user_func_array(array(object(WelcomeController), 'finalTest'), array()) in compiled.php line 8287
at Controller->callAction('finalTest', array()) in compiled.php line 8354
at ControllerDispatcher->call(object(WelcomeController), object(Route), 'finalTest') in compiled.php line 8333
at ControllerDispatcher->Illuminate\Routing\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in compiled.php line 8952
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in RedirectIfAuthenticated.php line 41
at RedirectIfAuthenticated->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in compiled.php line 8935
at Pipeline->then(object(Closure)) in compiled.php line 8334
at ControllerDispatcher->callWithinStack(object(WelcomeController), object(Route), object(Request), 'finalTest') in compiled.php line 8320
at ControllerDispatcher->dispatch(object(Route), object(Request), 'App\Http\Controllers\WelcomeController', 'finalTest') in compiled.php line 7317
at Route->runWithCustomDispatcher(object(Request)) in compiled.php line 7288
at Route->run(object(Request)) in compiled.php line 6954
at Router->Illuminate\Routing\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in compiled.php line 8952
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in compiled.php line 8935
at Pipeline->then(object(Closure)) in compiled.php line 6955
at Router->runRouteWithinStack(object(Route), object(Request)) in compiled.php line 6944
at Router->dispatchToRoute(object(Request)) in compiled.php line 6929
at Router->dispatch(object(Request)) in compiled.php line 1935
at Kernel->Illuminate\Foundation\Http\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in compiled.php line 8952
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in compiled.php line 2438
at VerifyCsrfToken->handle(object(Request), object(Closure)) in VerifyCsrfToken.php line 17
at VerifyCsrfToken->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in compiled.php line 12083
at ShareErrorsFromSession->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in compiled.php line 10785
at StartSession->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in compiled.php line 11789
at AddQueuedCookiesToResponse->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in compiled.php line 11738
at EncryptCookies->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in compiled.php line 2478
at CheckForMaintenanceMode->handle(object(Request), object(Closure)) in compiled.php line 8944
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in compiled.php line 8935
at Pipeline->then(object(Closure)) in compiled.php line 1891
at Kernel->sendRequestThroughRouter(object(Request)) in compiled.php line 1880
at Kernel->handle(object(Request)) in index.php line 53

Storing entity converts primary key to integer

When storing an entity the primary key gets converted to an integer which destroys the key's value if it's a string e.g. a UUID. I'm not sure where this happens, I can't find where it get's converted.

Impossible to write a Driver thats not Illuminate

Thank you so much for this package, I am glad to see a non-active record (and non-doctrine) option for Laravel. I ran into a bit of a problem:

Since Driver, needs a DB Adapter, which in turn needs QueryAdapter (all interfaces), things break down once you get to QueryAdapter. It seems like you guys cheat and just pass through directly to Laravel's Query Builder, which is wonderful if your intent is to only use Illuminate, but useless if we were to implement our own (say rethinkdb). Any plans to fix that issue?

Great stuff otherwise and I'm excited to implement this when talking to more traditional dbs.

Eager-loading problems

I've got a custom Repository class:

class Repository extends AnalogueRepository {
    public function with(array $relations) {
        return $this->mapper->with($relations);
    }
}

When I use $entity = $repository->with(['relation'])->find($id); it works as expected, but when I use $repository->with(['relation'])->firstMatching(['attribute' => $value]) it fails with this error:

Call to undefined method Illuminate\Database\Query\Builder::firstMatching()

Do I have to implement the eager loading myself or in another way to get eager loaded relations using firstMatching etc. ?

[discussion] I wonder if very pure DDD is really the best goal to pursue

The reason I wonder this is because it makes so many convenient methods impossible. I just found out that of course the mapper doesn't offer a firstOrCreate() method and it's also not really possible to add it, because that would mean skipping the __construct() method which means jeopardizing the entity's integrity.

So I'm starting to think that analogue is making development less convenient in name of some pretty principle. How do you see that?

Polymorphic relationships don't seem to work in both directions

I've got a Cue entity with the following CueMap:

<?php

namespace App\Domain\Entities;

use Analogue\ORM\EntityMap;

class CueMap extends EntityMap
{
    public $timestamps = true;

    public function documents(Cue $cue)
    {
        return $this->morphMany($cue, Document::class, "documentable");
    }

    public function cueable(Cue $cue)
    {
        return $this->morphTo($cue);
    }
}

This has a polymorphic relationship with various other entities (Film, TvProgramme).

I can do $film->cues and it returns all of the cues for the $film entity. However, if I try and do $cue->cueable I get back the $cue object, not the related object.

[feature request] make mass-assignment possible and easy

In your example in the REAME.md you show how pretty code can look when you can just do something like $gallery = new Gallery('Trip to India') and $photo = new Photo($file).

However, I notice in daily life with Laravel I rarely use that. I much more often use form-data to populate new entities and I really liked that with Eloquent I could mass-assign that like so: $user = new User($request->all()). And because I had set a $fillable property on User I knew that I wouldn't populate non-existing attributes or special attributes. And even all the right mutators would be called.

I think that use-case is much more likely in daily life. What do you think?

And I took it even one step further. I made my own base-eloquent-model in which I had my own constructor like so:

public function __construct(Request $request)
{
    parent::construct($request->input(class_basename($this)));
}

This way, in my controllers I could do something like:

public function store(CreateUserRequest $request, User $user, Person $person)
{
    $person->save();
    $user->associate($person);
    $user->save();
}

Because just by type-hinting all that stuff I knew that the data had been validated and correctly mass-assigned to the respective models. That is just so freaking convenient, can we please build something similar into Analogue?

In case you're wondering, this is basically what my form would look like:

<form>
    <input name="Person[name]" type="text">
    <input name="Person[infix]" type="text">
    <input name="Person[surname]" type="text">

    <input name="User[email]" type="email">
    <input name="User[password]" type="password">
    <input name="User[password_confirmation]" type="password">
</form>

Delete command doesn't return anything

Executing $repository->delete($entity) doesn't return anything. Wouldn't it be nice to have it return TRUE on success?

What I wanted to do is this:

if(!$repository->delete($entity)) {
    return response(NULL, 500);
}

For now this doesn't work because it doesn't return any value.

What do you think?

(Feature Request) Table Inheritance

Hey, I was wondering if there is (currently) a way to implement table inheritance (single, join, etc) with Analogue, and if not, maybe we could hash out some ideas here. From looking at some of the underlying system classes, I would assume that this is not going to be trivial to implement. If there's anything I can do to help, please let me know. I think having a nice and clean way to do STI with Analogue would help a lot of people or are transitioning from AR to DM in their Laravel based apps and could really strengthen support for this project.

ValueObject custom attribute names

I has 3 attribute in my database table:

  • cost
  • cost_month
  • cost_mimial

Cost VO class like:

class Cost extends ValueObject
{
    public function __construct($connection, $minimal, $month)
    {
        $this->connection = $connection;
        $this->minimal = $minimal;
        $this->month = $month;
    }
}

And Mapper:

class CostMap extends ValueMap
{
    protected $attributes = [
        'connection',
        'minimal',
        'month'
    ];
}

How map cost attribute to cost_connection attribute? if that possibility.

If not - what about this syntax?

class CostMap extends ValueMap
{
    // `[int] => [attribute]` converts to `voname_[attribute]` => `[attribute]`
    // `[string] => [attribute]` converts to `[string] => [attribute]`
    protected $attributes = [
        'cost' => 'connection',
        'minimal',
        'month'
    ];
}

Using a public method on a Mapper with an argument "automagically" becomes a relationship

If, for some reason, you require to append a method to your mapper, it will be evaluated as potential relationship. This is fine, but if that method happens to have one or more arguments and the first one happens to not be an object, an error will be hit where we attempt to call ->getClass()->implementsInterface('Analogue\ORM\Mappable') on a scalar type, which throws an exception.

The case can be found on Analogue\ORM\System\MapInitializer@61.

Add support for Postgres sequences

Okay, let me explain why is this really MUST be implemented.

As you may know Postgres uses sequences to generate auto-incremented id's, that's more reliable method to implement auto-increments than MySQL's one. This gives us more control over lastInsertedId command.

Why you really need to implement this feature? Because of the way how Postgres executes commands, sometimes there is something else saving in paraller of other row and that gives a collision (from our perspective), more known as race condition, in that situation when we call lastInsertedId w/o defining from which sequence we want it we will get id of a wrong row in wrong table or get just NULL because row saved in parallel has no primary key.

So in conclusion: sequence feature will eliminate race conditions.

Here is a quote from a php's manual for PDO::lastInsertId:

Returns the ID of the last inserted row, or the last value from a sequence object, depending on the underlying driver. For example, PDO_PGSQL requires you to specify the name of a sequence object for the name parameter.

Laravel already supports it

Injecting custom repository throws error

When Laravel tries to auto-inject a dependent custom repository class I get Unresolvable dependency resolving [Parameter #0 [ <required> $dsn ]] in class PDO. Seems like that Laravel tries to recursively inject dependencies into the classes constructors and fails for the PDO class.

UTF-8 Error

This is a weird one. Everything's been working fine. Haven't made any changes to the code or database and haven't touched composer for a while.

Now I'm getting UTF-8 errors when trying to access a relation:

SQLSTATE[HY000]: General error: 1300 Invalid utf8 character string: 'square\xF0")' (SQL: select *, `id` from `comments` where `deleted_at` is null and `comments`.`square�")` = 1)

FatalErrorException in EntityMap.php line 593: Call to a member function mapper() on null

This is my folder structure:

app /
    Model /
        Entities /
            Actor.php
            Person.php
        Mappers /
            ActorMap.php
        Repositories /
            ActorRepository.php
            Repository.php

ActorRepository extends the abstract Repository class which of course extends Analogue\ORM\Repository. And in my AppServiceProvider I register all the necessary entities and mappers. But when I run the code I get the error that's in the title of this thread.

It all seems to go wrong after it's calling the belongsTo method in my ActorMap, which looks like this:

public function person(Actor $actor)
{
    return $this->belongsTo($actor, Person::class);
}

Can anybody tell me what's going wrong?

edit: the error seems to go away when I move the mappers in the same folder and namespace as the entities. But I like to have them separate... :-(

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.