I think you're right. It's not possible to do what you want without a dirty hack.. Could you share us why you want to do this edge case? I just can't imagine a use case. There're might be a good pattern for your use case already..
anyway, this is super super super super super dirty hack, but it works..
$aClass = "class Migration extends Eloquent {} return new Migration;";
$migration = eval($aClass);
dd($migration); // this will give you Migration model, which represents 'migrations' table.
Thanks for the answer, moon.
Yup, that's exactly what i was thinking when i said "ugly hack". And then i said to myself: "Oh no Canta! Stop it right there! You promised not to do this kind of things anymore!". And that's how i ended here asking.
About sharing what i'm doing... Basically, i want my CRUD fully automated, without having to code no trivial change on the DB schema anywhere in my classes. I want auto validation, atomic user permissions, common API, and automatic UI forms, for any action on any table on a selected DB, without me to having to code things like "$this->hasOne('stuff')" (or, worse, to create a class for that, and then having to dump-autoload it) when that's already stated in the table definition in the DB schema; i want my ORM tool to read that already stated information and dinamically instantiate a ready-to-use class that i may subclass only if i need to.
You think that's and "edge case", as in "almost wrong"? Please, if you may write it, i would love to read why. So far, i've working like that for years and i'm very happy with the results of that approach. I don't want to keep on doing this if it's a known bad thing to do.
Anyway, i've been doing this with my own HORRIBLE orm classes, and now i'm working with Laravel (and i love it). I want to use Laravel fot this. If Eloquent is not the answer, maybe there's another way inside Laravel? Right now i'm adapting my own ORM/Model classes to Query Builder, but i would love to use some tool already there for the task.
No problem!
I would not even think about trying your way because of a few reasons.
Well... i think i'm gonna cry. :(
I obviously will have to think about this points (and i will). However, for the sake of the thread's discussion, i'll answer them based on my experience. But i fear this turns into some flame war about programming philosopy, so please let me add a disclaimer: i'm a very ignorant person, i'm not trying to convince anyone my way's the right way. On the contrary, i'm just learning what's wrong about it.
Oh, and another disclaimer: i speak spanish, not english.
I'm not verse enough in the matter to fully understand the difference beetween ORM patterns like ActiveRecord or DataMapper, for naming two i constantly see when googling about. Same goes for repositories, which i saw when looking for the issue of this thread, and never before as desing pattern. That said, i guess generally speaking it's not that hard to reflect changes in a table in a model. If i don't want to do it, it's not because i think it's a hard thing to do: it's because i feel i'm doing twice or more the same task if i already changed the table. I believe i must work only in the strictly neccesary, like real business logic for example, or investigation, or learning. This is kinda abstract stated this way, but it's not when a guy who knows nothing about Laravel (or PHP for the matter) somewhere out of my office changes the DB but not the code, some fella on the desk in front of me has to do that task on a model, some other guy has to add a field check on an IF in some JS function at some other time, and then a trivial thing like adding a varchar to a mysql table starts to generate human mistakes when any of them has to do something else more important at that same time, or just doesn't fully understand the change, or etc. I really don't understand why should anybody move a finger for something that can be read automatically from the table structure in a generic way for any table in any db; i do understand why somebody will have to move a finger, and more, when that's NOT the case.
About the autoload: so far, when anybody adds a model, we have to dump-autoload the app. Don't fully understand it yet, sorry: just know it's the way the app i'm working behaves. It's trivial now, because i've scripted repo hooks and that thing is automatic. I did it with Artisan. But it was a headache for my colleages in my new job, to discover after a deploy that the app was not finding a model class recently implemented.
Technically, i'm not looking for defining classes on the fly, but to use one single class to common tasks. So, everyone would know what's going on with that one single class, instead to go to read a custom model. For example: " new Model($table)->render('html') ", should be the same for table "Users" and table "Whatever", and it's not cryptic at all, and everyone knows what to expect from that: the same as extending Model in, let's say "User" class, and calling " new User->find($id)->render('html') ". I want to create a "User" class only if User have some custom logic i need to handle; like "sending an email when it calls the save() method", or any not-standar-for-a-table thing like that.
Like i said, i don't design all. I have an app i didn't make, and it works based on tables. Anyway, "adding tables" is not only about being designing like a crazy man, but also about systems growing and apps getting more complex than they were initially. It happens with anything that's not a very simple app in my experience. And i tend to generalize tables, but i don't always have the time to think the right way to do it, so sometimes i implement crappy tables to solve something fast, sometimes i re-think what i did before and then the table changes, and so on.
Well... i don't know if i'm using an active record, i just know the result i want from the tool. I guess then i'm not looking for an active record, but something else. And i may agree on that models should contain logic, but i don't want to code many times the same logic when i don't need to; i even feel it makes me a bad programmer, because i'm using my time in a not productive way. I believe that everything that CAN be automated, SHOULD be automated, and we should work on the exceptions and fine tuning cases. So, my models have logic, but the greater part of that logic they share beetween them.
If you think about it, it's basically the same as extending Eloquent, just that Eloquent doesn't let you do " new Eloquent('users'); ". I want exactly the same, but with that extra funcionality: nothing fancy. I really don't feel i'm breaking any good practice with that.
My apologies if I were a little bit aggressive. I re-read what I wrote and It's a little bit aggressive or too much.
No war at all. I mean..I totally understand what you're talking about. It's not easy to develop in a group. There're just many people with different knowledge. My point was not that your method doesn't work. Obviously, that works. My point is that..that's not what most people do for reasons. I just wanted to point out since you asked. I think all we want is improve even if it's just a little.
I still don't agree with it. I think we're talking about a software that will live at least a year (I hope). I honestly don't know what to say.
It's not the same task. You have a change in a way how your software works. Making changes in your code to reflect that change is necessary.
new Model($table)->render($html)? I'm not exactly sure what it does, but it doesn't look like...a model that renders..an html..?
There's difference between automating something and hacking something.
Eloquent doesn't let you do because of a reason. We use a tool to do a task. Eloquent is not the tool you want to use to do what you want. Differentiating purpose of a tool and use it properly is our job as a developer.
I sincerely hope you or one of your team members look at the codebase and think about the design if all of your team members are doing what you've described (I know..how dare I say this?)
In the end, it's your business and your application. It's up to you.
Updated: I think you're essential...using table gateway (not strictly)..not ORM in your case..FYI
Moon, thanks again for the answer. Your previous comment didn't felt aggresive at all, i just suddenly felt that i may be doing something really wrong.
I respect what you say. After thinking about it, i'm now doubting about what i'm doing, specially regarding this "adapting to any database" approach instead of applying some strict refactor/normalization and going to polymorphic and flexible/abarcative relations.
Thing is, i DON'T doubt about the productive results of this approach, as in my experience i had very few successful chances of dedicate time to apply heavy refactors to existing systems and i'm afraid that making software fast enough is what pays the bills: mine, and my partners's, with which i feel i have a responsability bigger than the one i have with the programming good practices.
It's a problem. I can't just stop production an tell everyone: "hey! this is not the way!". Once in a team, i have to play ball, and the choices are always limited: specially in time.
Anyway, i'm also begining to feel that our differences in perspective comes from definitions. I mean, i was thinking last night, "maybe i'm not even talking about a model here, maybe it has some other name"; as you just suggested. Which, IMHO, is a recurrent thing in online debate; i remember, when i was learning years ago what MVC was about, that i felt there wasn't two single persons in the entire world describing in the same way what are the limits of MVC layers and how they should be done. My pragmatic conclusion is that i need to take a path and walk it in deep to fully understand what my partners are talking about, and so i end up with my own definition for common terms: and that definition may be just play wrong.
So, let's try to clarify the definitions:
I call "Model" a generic OOP class that handles mostly data structure logic. Which, in real life, that's usually just CRUD in some DB table, and other common infraestructure/maintenace operations over tables like searches and working with the results format ("toJson", "toArray", and so on). This is coherent with what i pretend from a Model class, and i also find it coherent with what Eloquent offers, hence this discussion.
I say ORM is ANY mechanism that maps/translates relational tables to objects. I understand ORM is an actively vague term that encapsulates a series of possible techniques. That, again, is coherent to both what i'm looking for and what Eloquent offers.
I know there are different ORM patterns out there, and Eloquent in particular implements ActiveRecord, which have its principles and i can't just ask for any random functionality in it. But i don't know in detail the principles of ActiveRecord (or any other common ORM pattern for the matter, i just know they do exist), and that's why i end up asking this bizarre (or "edge case") questions. I mean, i'm not trying to argue about what Eloquent should do; that's an Eloquent problem, not mine.
The only BIG difference between my idea of a model an Eloquent's proposal, is that Eloquent's Model is an abstract class instead of an instantiable one, and it has no mechanism for real-time directly-from-table-parsing instantiation (factory): it requires (and does not gives you the option of) explicit definition. That's REALLY the only difference i see, and that's why i'm here scratching my head about the whys of that.
So, as you could see, it is very useful to me to read about the existance of concepts like "table gateway", which i'll look foward to learn.
I'll ask you for one thing (of course if you have the time and the will for it). You say "Eloquent doesn't let you do because of a reason". I don't fully understand that reason. I believe i do understand your arguments in this debate, but i understand that's your opinion and not neccesarelly Eloquent's principles. I mean, my guess is those reasons lie behind ActiveRecord principles or some other pattern that i would like to understand better. So, do you know about where can i read about Eloquent's reason for not allowing to do what i want to do?
Thanks.
Before we continue discussing about it, I would like to confirm a few things just to double check that I understand correctly.
return Model::create($some_table_name);
When I do Model::create('tags'), should I get a Tag object, which extends Eloquent? Am I correct on that? If so, I assume you want to do it because you want to use Eloquent specific methods such as find(), find_all(), where(), and_where(). Am I correct on that?
I mean..it's not like Eloquent is intentionally designed to prevent your method. It's just designed in a common way.
Just wondering, what framework did allow you to do what you're doing? Could you name one?
Yeah, that's what i want and why i want it. :)
To add: i have my own libs with my own custom interfaces to do that, and i want to do the same with Laravel classes (in this case, Eloquent models).
And another detail: It doesn't need to be an instance of a "Tag" object, but could also be an instance a, lets say, "Model", or "BaseModel", or "AutoModel", or whatever, which DO extend Eloquent (or at least implements the same interfases), and has something like a "table" property set to "tag". Inside "Model::create" would be written the IF block of the original post in this thread.
About frameworks that do this... well, honestly, i don't know =/ A few years ago i saw that Django or SQLAlchemy did this kind of things in Python... i guess it was "SQL-Soup", a plugin or library or something like that. Didn't used it, just crossed it while exploring what ORM tools where out there. I could swear i saw more things like this before, but can't think of any name right now.
The other tools i know that do ALMOST the same are more common. Propel, Doctrine, and some tools for C#, PERL, and other languages. Or even a "migration generation from existing db" Artisan command i've tested a few weeks ago. What they do is to automate the code generation of intermediary files: Model files, or database map files. The first case i did before, the second is basically the same as what i'm looking for but reading an XML (or the likes) file instead of reading information_schema directly. This last comment deserves an obvious performance discussion, but my point right now is that is essentially the same mechanism but dealing with files instead of volatile data. I don't know if should be understood as a case of "instrospection" or "reflection".
I've used Propel, and Doctrine. I really don't think we're talking about the same thing. Migration generators do make an actual file. That's totally different than what you're trying to do on the fly. Doctrine does require you to define a class in a file. The docblock you're talking about is not the same thing. It's completely opposite actually. Whenever a person look at the docblock in POPO object in doctrine, they know exactly what that field is for and where it comes from because docblock requires you to define it.
I really disagree that they do ALMOST the same thing. They're totally different. They might look similar, but they're not similar in how/why/where they're being used.
I've looked at SQL-Soup briefly. I feel like it's almost like a query builder. There's just no way you can add domain logic to the object that's generated on the fly without violating best practices. Look at the usage http://sqlsoup.readthedocs.org/en/latest/tutorial.html#basic-table-usage.
You can create something similar with Laravel query builder or any other query builders.
SQLSoup:
users = db.users.all();
Laravel Querybuilder
$users = DB::table('users')->get();
Are you looking for this...??
I think the best way for you to do something like this would be by creating a "generic" model class and set the table property on it manually. Then you should be able to do:
$foo = new GenericModel();
$foo->setTable('foo_table');
$foo->bar = 'A';
$foo->baz = 'B';
$foo->some_other_column = 'C';
$foo->save();
But really, relying on purely auto-generated code is a dangerous path. You'll eventually end up with something like the Query of Despair
@thepsion5 // that's nice. I didn't know 'setTable' is a public method. Thank you! Thank you for the 'Query of Despair' link as well :)
@moon: before continue, know that i feel very grateful for your patience. It's important to me to understand this tools, and you're helping me a lot.
moon0326 said:
I've used Propel, and Doctrine. I really don't think we're talking about the same thing. Migration generators do make an actual file. That's totally different than what you're trying to do on the fly. Doctrine does require you to define a class in a file. The docblock you're talking about is not the same thing. It's completely opposite actually. Whenever a person look at the docblock in POPO object in doctrine, they know exactly what that field is for and where it comes from because docblock requires you to define it.
I really disagree that they do ALMOST the same thing. They're totally different. They might look similar, but they're not similar in how/why/where they're being used.
Let's take this example: http://propelorm.org/documentation/02-buildtime.html
You say reading that XML file is completely opposite from reading this other output, http://dev.mysql.com/doc/refman/5.6/en/mysqldump.html#option_mysqldump_xml , which is nothing but the schema definition?
I don't get it. It looks like the same thing to me. And i don't find much differences (with the exception of possible performance issues) in reading that information from an XML file and from a DB query.
Then, there are the model classes files, generated from that xml. The first thing i did when tried to make my own ORM tool was exactly that: parsing the output of mysqldump --xml and process it using XSLT to generate Model classes in files. It worked flawlessly until the DB started to change every week and re-creation of the classes automatically (which was the reason of using ORM in the first time) became non trivial as the model classes begun to include custom logic and so i had to make my code generation tool more complex in order to understand diffs. Then came an intermediate layer inheriting from the autogenerated model files; the intermediate would have the logic, and the auto-generated files would have the table definition. And after a few times of re-generating those dumb structure-only files, i begun to ask myself what was the point of using a file at all for that. So i took away those files, readed structure from the schema, the logic would stay in that "intermediate layer" which then formally became known as "Model classes", the Model classes kepth on inherithing from a generic and instantiable base class containing all the methods for a Model but alowing to inspect a table for dinamyc instantiation, and so there was the logic stated in a file but only when i needed a file.
That was fine during years, those techniques grow up into libs, and everything was nice with the technique. Then enters Laravel, with Eloquent, and everything looks wonderful. But before creating this thread, i had this experience: i went to add a relation to "User" model. It was a "hasManyThrough" relation: i'm adding "permissions" to "users", so i have a "users_permissions" table. Then i state the "hasManyThrough" pointing "users_permissions", and it returns an error: "there's no users_permissions class".
So, no, i don't want to create a file that says "Hi, i'm users_permissions, and i do nothing. Nice to meet you". I actually find it a bad practice, because it only generates maintenace of absolutelly useless (IMO) files. That's why i want some generic model for THOSE cases (and not for EVERY case). Because i want to have a few useful models, maybe a bunch, but definitely not lots of dumb ones. I want to go to this app model's and tell them where to look for some data that belongs to them in the database tables using OOP and ORM techniques. I believe Eloquent if perfect for that last task, except it seems to have nothing like that generic model i need.
I do understand that other people disagree with this approach, and i sincerelly respect it. I do understand that Eloquent, Active Record, and/or other tools, do take another approach, and that is absolutelly fine by me. But i DO NOT understand why the approach i'm choosing is wrong, and i feel farther from understand it when i check again my positive experience.
I've looked at SQL-Soup briefly. I feel like it's almost like a query builder. There's just no way you can add domain logic to the object that's generated on the fly without violating best practices. Look at the usage http://sqlsoup.readthedocs.org/en/latest/tutorial.html#basic-table-usage.
You can create something similar with Laravel query builder or any other query builders.
SQLSoup:
users = db.users.all();
Laravel Querybuilder
$users = DB::table('users')->get();
Are you looking for this...??
- Thank you for introducing SQLSoup. I'm learning Python and SQLSoup looks interesting.
Indeed, i'm doubting myself if Eloquent's the key Laravel class here or it is Query Builder. Looks tricky. Seems you were right when you called all this "edge case". :P
To answer your question: kind of, i'm not really sure. I guess, seeing the example, that the answer would be "i need to be able to create classes that inherits from DB::table".
That's "Fluent", right?
thepsion5 said:
I think the best way for you to do something like this would be by creating a "generic" model class and set the table property on it manually. Then you should be able to do:
$foo = new GenericModel(); $foo->setTable('foo_table'); $foo->bar = 'A'; $foo->baz = 'B'; $foo->some_other_column = 'C'; $foo->save();
But really, relying on purely auto-generated code is a dangerous path. You'll eventually end up with something like the Query of Despair
Yup, that's it. But when i tried, it didn't worked:
class dummy extends Eloquent{
}
$user = new dummy();
$user->setTable("users");
$user->find(1);
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'db.dummy' doesn't exist (SQL: select * from dummy
where id
= 1 limit 1)
Perhaps disabling the naming convention somehow? Because otherwise it all goes back to the eval trick...
EDIT: Oh, i love TDWTF too, it's fantastic.
I guess I was confused by your definition of a 'model'. A model in MVC..contains domain logics. If you're using model like how you're using it, that's...not a model in my opinion. That's just a table gateway-like object.
If you like @thepsion5's solution, yeah, I think that's totally fine use case.
So..@thepsion5's code won't work since Eloquent Model class creates a new object with 'new static' when you call 'find' or other static methods.
File location: vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
public static function find($id, $columns = array('*'))
{
if (is_array($id) && empty($id)) return new Collection;
$instance = new static;
return $instance->newQuery()->find($id, $columns);
}
Try this version of Dummy class.
class Dummy extends Eloquent {
protected static $_table;
public function setTable($table)
{
static::$_table = $table;
}
public function getTable()
{
return static::$_table;
}
}
$dummy = new Dummy;
$dummy->setTable('posts');
$post = $dummy->find(13);
dd($post);
Keep it mind though...the $post will have 'Dummy' type.
Thank you for your patience and it was helpful for me as well. Enjoy your new framework :) and welcome!
So many possibilities...
Thank you SO much. I did try stuff like this, but i was missing Eloquent's models static declarations in detail and ended thinking to start hacking it ugly.
BTW, for the sake of the thread, i'll not use @thepsion5's pattern but probably some static factory logic like the things in the original post.
I mean, something like this:
class ModelBuilder extends Eloquent {
protected static $_table;
public static function fromTable($table, $parms = Array()){
$ret = null;
if (class_exists($table)){
$ret = new $table($parms);
} else {
$ret = new static($parms);
$ret->setTable($table);
}
return $ret;
}
public function setTable($table)
{
static::$_table = $table;
}
public function getTable()
{
return static::$_table;
}
}
$user = ModelBuilder::fromTable("users")->find(1);
dd($user)
As you can see, i'll use it (as its smell was telling us) LIKE a "DB::table"; but when there's a formal Model with its custom logic (written in a class file), "find" will be the one stated in that Model, and not necesarelly Eloquent's. This may sound strange for a "find" method, but it's not for a "save" one for example. And this way i can generalize common operations with a custom BaseModel with my desired added generic logic, ModelBuilder inherits from it, and ta-da, i get what i wanted. Got to polish details, but looks promising.
I feel Laravel is wonderful.
Yes, that looks better for your use cases.
Have a great weekend :)
Indeed, it is!!!!
Hi all,
Thanks a lot for this post this helped me a lot.
I am implementing a system where admins can basically define Models using a backend which stores the properties in a DB while dynamically creating tables for each model. Then the frontend will instantiate the model at runtime using the DB parameters.
Using the class suggested by Canta allows me to define the table name at runtime and to use Laravel's model features for create / edit / show operations.
I am now working on creating relationships at runtime, which mean doing something like this:
$user_model = ModelBuilder::fromTable("users");
$role_model = ModelBuilder::fromTable("roles");
$user_model->roles = new BelongsToMany($role_model->newQuery(), $user_model, $pivot_table,
$foreignKey, $otherKey);
The problem I'm having is that when instantiate role_model the static attribute will of course be replaced and both will end up with the same table name in future calls.
I guess the solution would be to create an inherited class at runtime using Runkit or eval but it looks a bit dirty. At the same time I guess this is a hack so..
Did any of you guys ever tried to make something like that? What are your thoughts?
Thanks!
DODMax said:
Hi all,
Thanks a lot for this post this helped me a lot.
I am implementing a system where admins can basically define Models using a backend which stores the properties in a DB while dynamically creating tables for each model. Then the frontend will instantiate the model at runtime using the DB parameters.
Using the class suggested by Canta allows me to define the table name at runtime and to use Laravel's model features for create / edit / show operations.I am now working on creating relationships at runtime, which mean doing something like this:
$user_model = ModelBuilder::fromTable("users"); $role_model = ModelBuilder::fromTable("roles"); $user_model->roles = new BelongsToMany($role_model->newQuery(), $user_model, $pivot_table, $foreignKey, $otherKey);
The problem I'm having is that when instantiate role_model the static attribute will of course be replaced and both will end up with the same table name in future calls.
I guess the solution would be to create an inherited class at runtime using Runkit or eval but it looks a bit dirty. At the same time I guess this is a hack so..
Did any of you guys ever tried to make something like that? What are your thoughts?
Thanks!
Did u find any solution? I've been fighting with it couple days, have some half-working tests, but nothing for profuction.
Dudes, this issue isn't solved.
Sign in to participate in this thread!
The Laravel portal for problem solving, knowledge sharing and community building.
The community