TIP: Use Markdown or, <pre> for multi line code blocks / <code> for inline code.
These forums are read-only and for archival purposes only!
Please join our new forums at discourse.kohanaframework.org
New AutoModeler Rewrite
  • Hey all,

    Anyone following/using the AutoModeler library might be interested in some new developments I'm doing:

    https://github.com/zombor/Auto-Modeler/compare/develop...feature/5.0-DAO

    I'm rewriting AutoModeler to utilize a DataMapper or DAO pattern. This will let me actually keep the core testable (you can see everything is tested so far), and actually let you write useful unit tests for your models.

    The main benefit of this is abstracting the database access away from your business logic. There will be a DAO object for each table, along with a model to go along with it, which will contain your business logic (which is easily testable).

    Any feedback on the work I'm doing is greatly appreciated.

  • Hi Zombor,

    Will this rewrite aim Kohana 3.0+ or 3.3+? I will take a look of it and see if I can help on anything.

    Thanks,

    Nano.

  • I actually think it will work with any version. It doest extend any kohana classes. The tests don't actually use anything in kohana at all. It will actually be very easy to write a DAO class to work with any backend system or framework.

  • This is great, is the idea that you can just inject a model into any DAO method? Will there be an interface for the DAOs, I see it's CRUD for the moment for DAO_Database, but Database_Query_Builder instances are required by the methods. Are you planning to generalize these to allow easier swapping of data storage type as required (e.g. DAO_Database <-> DAO_File?), or is that outside of the scope of AutoModeler?

  • is the idea that you can just inject a model into any DAO method?

    Yes. Models are for business logic, not database access. I'll be implementing getting objects via a Repository or Gateway pattern.

    but Database_Query_Builder instances are required by the methods.

    They aren't required by the end user. The parameters need to be there for testing, since Kohana_Database is very horrible in this aspect. For instance, if I wrote a Real-DB DAO backend, they wouldn't be needed.

    Are you planning to generalize these to allow easier swapping of data storage type as required

    Yes, the idea is that you could just shoot any model into any DAO class for processing, be it Database, XML, CSV, Mongo, etc.

  • Ah great, I see, so the interface definition would just be:

     public function create(AutoModeler_Model $model);
    

    Looking forward to seeing how it all works out, I'll certainly be testing it as it matures.

    Where do you set $_table_name?

  • Where do you set $_table_name?

    You extend the DAO for each table you need. You'll end up with two classes for every "logical unit".

  • Couldn't we just pass it in the constructor, or use a factory?

  • Possibly :)

    It really depends how much other "table" stuff the DAO needs. I like the idea of using factories for it though.

  • I started writing a library that follows the DataMapper/Object pattern called cacuts.

    I got it to what I would call a beta state, but not ready for production by any means. As my database heavy projects wound down, so did my development on the library so there it sits.

    I'm not sure if cactus will be any help for you in AutoModeler, but you never know...

  • Did anybody ever look at gabriel1836/gacela / gabriel1836/kohana-kacela ? Though it's PDO only it does the data-mapper thing.

  • Here's a factory implementation: https://github.com/zombor/Auto-Modeler/blob/feature/5.0-DAO/tests/automodeler/dao/database.php#L307

    It's working out in a way that you can programmatically create models and dao objects, without even having anything saved as files on the filesystem. Kind of nice.

  • Kacela only supports PDO because:

    1) PDO comes stock after PHP 5.2.x. Kacela requires 5.3. 2) PDO provides support for all of the major databases so that Kacela can rely on PDO functionality rather than having to work around functionality that may or may not exist in another library.

    That said, NoSQL support is in the works for Kacela which will obviously use a different engine.

  • @zombor Very nice, I think a lot will depend on how you implement queries/criteria in an agnostic way, and also I'd really welcome a full implementation of at least one other data source apart from DB ... XML would be handy, even if only just to show how/if it can be done. This is what seems to be lacking in Gacela currently - it still seems very bound to the assumptions of a database.

  • Also, shouldn't it be possible in principle to have this generic version of the factory method:

    $source = new Database;
    $context = array('table_name' => 'foo');
    
    $dao = AutoModeler::DAO($source, $context);
    
    or:
    
    $source = simplexml_load_file('test.xml');
    $context = NULL;
    
    $dao = AutoModeler::DAO($source, $context);
    

    ... where the factory can inspect the injected object type and create an appropriate mapper for it? Otherwise the app needs to know about the implementation if you have to use AutoModeler_DAO_Database::factory($database, 'users') each time. Matching these object types to mappers could be handled by config files, I guess.

  • DAO sounds good. The only thing that I really miss with auto_modeler is build in support for validation labels. Many times I'll be validating a field with some meaningless name and get a model validation error like : Error: DIRWATCH must not be empty. It doesn't mean that much to the user.

  • You can set a custom message file for each model to do custom error messages.

  • @zombor; with this dao setup will you instantiate the dao and assign it to a $dao var in the controller then instead of the model? Or is it not the goal leaving out (all) dao stuff from the model?

    I currently use a more simple dao setup but am mixing it in my model(s). For example to load related data for a has_many relationship I create a dao instance inside my model. Think of a $dao = DAO::factory('authors'); $dao->getAll($this->data->id); inside a Book model.

  • You always pass in the model. Take a look at the docs/tests to get a feel of how to use it.

    The point is to remove the database junk from the model completely. The dao/gateway classes work with a model.

  • Yes, I see that (for create/update/delete), but maybe I don't get the full picture yet... where are the read actions? You won't be doing new Model_Book(2); anymore to create the model and load data? But:

    $model = new Model_Book;
    $model->set('id', 1); //could be passed when creating the new model, but ok...
    $dao = new DAO_Book;
    $updated_model = $dao->read($model);
    

    ?

  • Those are in the gateway class.

  • Of course.. I think I missed them in the tests and thought the gateway class was another idea of the dao implementation still in some development state.

    Keeping the 'book' example; where would you be storing relationships for a (model) book, for example authors? And how should the loading be implemented for such relations?

    I'm sorry asking all these questions, but I have plenty :)

  • There's no ORM implementation yet, but it would be going into a gateway class. Syntax would be something like:

    $books = $gateway->find_related('authors', $book);

  • I see (I think I also did not pick up the little sentence earlier "I'll be implementing getting objects via a Repository or Gateway pattern." I'm not fully aware of "official" patterns, but this is a known structure?

    Here they talk about repository pattern and that creating/deleting is part of the repository: http://css.dzone.com/books/practical-php-patterns/practical-php-patterns-8 ? I'm not a professionally trained on patterns, just read things every now & then and usually get lost as people describe patterns differently.

    I ended up using my own pattern which is a mix and mash of various ideas and probably wrong :) it's still abstracted though, but naming could be better.

  • I don't think the specific naming of the patterns is very important (gateway vs repository vs dao are all very alike), but rather the separation of concerns is what's important.

  • Yes, I agree. It's a bit hard to contribute really if you're not really sure what the principles are (like following a strict pattern or not).. so that's why is was picking your brain more or less to see where you was coming from.

  • I'm working on a demo app for v5: https://github.com/zombor/Auto-Modeler-Demo

    It's mostly to make sure the code I wrote works integrated with the whole system, but it will also serve as a demo app for users to look at to learn how to use the module properly.

    Once I get done with this, I'll release v5 as official.

  • Cool Thanks.
    I'm still a little puzzled about Retrieving data. Though I have it working, it didn't feel "right";

    class AutoModeler_Gateway_Database_User extends AutoModeler_Gateway_Database
    {
    
        public function process_load($model, $data)
        {
            $this->_process_load($model, $data);
        }
    
        public function process_load_state($model)
        {
            $this->_process_load_state($model);
        }
    
        public function load(AutoModeler_Model $model, $id)
        {
            $query = DB::select()->where('id', '=', '1');
            $this->_load_object($query, $model);
        }
    }
    

    Also; Any plans for a 3.3 (PSR-0) version?

    Keep up the good work. :)

  • @zombor: A feature I really liked about AM was the ability to save and update at the same time. After browsing trough the code I see it's no longer possible. Will this feature be removed in AM? I would really hate to see it go.

  • I'm still a little puzzled about Retrieving data. Though I have it working, it didn't feel "right";

    The idea is to create methods to fetch the specific data you need. Don't make load() public. It's protected for a reason :)

    Also; Any plans for a 3.3 (PSR-0) version?

    When 3.3 is final, I'll port it over.

    A feature I really liked about AM was the ability to save and update at the same time. After browsing trough the code I see it's no longer possible. Will this feature be removed in AM? I would really hate to see it go.

    I wasn't planning on making a generic save() method. In just about all cases, you should know the context of what you are saving.

  • @zombor: Of course but the easy part about AM was that AM was keeping track if it was a update or create. It makes saving forms really easy eg:

    $user = new Model_User($this->param('id'));
    $user->data(
        array(
            'email' => $this->request->post('email'),
            'password' => $this->request->post('password'),
        )
    );
    $dao = AutoModeler_DAO_Database::factory(Database::instance(), 'users');
    
    try
    {
        if (AutoModeler_Model::STATE_LOADED != $user->state())
        {
            $dao->create($user);
        }
        else
        {
            $dao->update($user);
        }
        $this->request->redirect('welcome/list');
    }
    catch (AutoModeler_Exception_Validation $e)
    {
        $view->errors = $e->as_array();
    }
    

    vs:

    $user = new Model_User($this->param('id'));
    $user->data(
        array(
            'email' => $this->request->post('email'),
            'password' => $this->request->post('password'),
        )
    );
    $dao = AutoModeler_DAO_Database::factory(Database::instance(), 'users');
    
    try
    {
        $dao->save($user);
        $this->request->redirect('welcome/list');
    }
    catch (AutoModeler_Exception_Validation $e)
    {
        $view->errors = $e->as_array();
    }
    

    I think it's a functionality that should be considered because it would support DRY and promotes simplicity

  • I'm not sure it's a good idea to do both create and update in the same action. Splitting them up into two actions eliminates the need for having them be combined.

  • Latest updates helped greatly. Thanks @zombor.

  • The unfortunate thing about making an abstract class with only protected methods, is that there's no good way to test it :)

  • I thought you had some code in place at github too that was dealing with related data (sort or primitive).. did you remove it because it was crap? I didn't have time earlier to check it. I still like this, it's like my current approach but the code is a bit more refined (though I'm not sure about the query builder code). The demo site setup is a nice gesture also; even though the code base is not so hard to understand.

    ++

  • I thought you had some code in place at github too that was dealing with related data (sort or primitive).. did you remove it because it was crap?

    I never pushed up any ORM code. I don't think it belongs in a gateway implementation. Spec-ing such code out is very hard too, which is indicative of it's use.

  • The demo site setup is a nice gesture also; even though the code base is not so hard to understand.

    I plan on making that repo a general "proper how to" of tricks for kohana using the tech in the README file. It's got kostache and auto modeler stuff in it right now.

  • I'm not sure it's a good idea to do both create and update in the same action. Splitting them up into two actions eliminates the need for having them be combined.
    

    @zombor You shouldn't put them in the same method but make a save method that combines them. This way you can pick what kind of behavior you want it to have. So leave create and update in AM but make an extra save function that combines those. The reason why I'm asking this is because most of the time you use a single form for both create and update. What I really liked about AM was that it also required a single method for saving that form.

  • because most of the time you use a single form for both create and update.

    It's fine to use a single form for it, but those forms should submit to two different actions. One action/use case shouldn't both create and update a model.

  • @zombor Why? I've always used a single method for both update and creating items. Everything is the same except the updating / creating part. It's for me part of DRY programming.

  • I don't use AM however I also share @rjd22 opinion :) Don't make it complicated unless you're forced to.

  • Simple frequently lends itself to a mess in the long run.

  • @zombor - will it work with MSSQL driver?

  • It uses the kohana database class by default. So you'd have to use the PDO driver and it should work fine.

  • After a lot of thought, I've decided to stop this rewrite and focus on a pure repository/gateway implementation. The fact that AM forces you to inherit your business objects from a third party base class really bothers me.

    New code is located at: https://github.com/zombor/Arden-PHP

  • Sounds interesting. Is this alpha, beta, almost production ready code? Can you provide some examples of using it?

  • The usage is pretty much the same as AMv5 was. The idea is that you just have POPO business objects now instead on inheriting from a third party base class. It's not done yet.

  • Is this still in the works or abandoned? I found some old git clone while cleaning up my system.. not sure if my deleted stuff was older than 7 months (last updated at github)

  • The code is out there, you can use it. There's no future work being done on it though.

  • The usage is pretty much the same as AMv5 was. The idea is that you just have POPO business objects now instead on inheriting from a third party base class. It's not done yet.

    Is Arden still a WIP? I am leaning towards having plain POPO objects as well so I like Arden as an option.

  • It's abandoned. You don't need a third party library to do this. Just use a repository pattern with kohana's query builder.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion