DataMapper 1.6.0 |
[eluser]GregX999[/eluser]
Yes, that's what I was trying to get at when I made the restaurant/menu examples. Code: $r->items->where('name', 'chicken')->get(); However, this makes it seem like you're after all the items with chicken that the restaurant "$r" has. (The "where" is acting on the items of a particular restaurant.) Perhaps something like this to get all the restaurants that have an item with "chicken": Code: $r->have('items', array('name'=>'chicken'))->get(); ("have" or "has" - would it matter?) Both of the examples above would be quite useful. Greg
[eluser]stensi[/eluser]
Good suggestion. This is something I noticed along the way but haven't had time to look at much. The solution I'm favouring is to have the related object start off completely empty on first access and you'd have to populate it much like you do the normal objects. For example, let's say we have an authors table with a One to Many relationship between the books table. If we wanted to see what books an author has made: Code: // Get author So yeah, the related objects work like any DataMapper object, except its queries will always relate to its parent object. Thoughts? UPDATE I've got the above way working properly now. I've also changed the get() method to be chainable, so you can do it this way if you want: Code: // Get author
[eluser]GregX999[/eluser]
That's pretty slick. Would this work to get a list of bookstores that sell an author's books? Code: $a = new Author(); And can I get a list of an author's books available in a certain city like this? Code: $a = new Author(); (I swear, I'm not trying to be a smart-ass!!) Greg
[eluser]stensi[/eluser]
No problem :-) Happy to have your questions since they've helped to tighten up and improve DataMapper. For the above question, unfortunately no, those wont work as is. Since related objects will be empty from now on, you'll need to do a get() to populate it with at least one record before going into a deeper related object (otherwise there's no parent record for the deeper related object to know what to relate to). So in your first snippet of code, you need a get() after the book, for example, I'll limit to getting 10 books: Code: $a = new Author(); Now, the above would only look at the bookstores for the first book, since there's nothing above to tell us to loop through all the books, and look at all the bookstores for each of those books. That's an important thing to take note of. There's no simple way I know of that would allow you to foreach through 1st level related objects and 2nd level (or more) related objects in the one foreach. I don't think that's do-able. Also note that related objects are only aware of their parent object and no higher (they're not aware of their parents parent object etc). If you wanted to look at all books the author has, and all the bookstores they're in, you'd have to do: Code: $a = new Author(); or you could do it this way: Code: $a = new Author(); Onto your second snippet, that wont work for the reasons stated above, but also, because you're missing a get() call after your first where call and you're using the original author object as part of a where clause (not possible - you would need to pass the authors name instead). Am I right in seeing you're almost doing a full relationship loop, from the author through his books, the bookstores they belong to, and then the books in the bookstores that belong to the author? Not that you'd ever really want to do this but here's how it can be done: Code: $a = new Author(); or Code: $a = new Author();
[eluser]GregX999[/eluser]
Could you get rid of the requirement to call "get" on a relationship by checking to see if that relationship has been populated, and if not, by populating it? So you could do this: Code: $a = new Author(); Is there a way that $a could know that "books" is empty so it should perform a "get" on it. Or maybe if $a just knows it "has many" books it can just always do the "get" if that function already handles caching. Then I would think you COULD chain relationships (as long as each relationship was a "has one" - except for the last on in the chain which could be anything): Code: // When viewing an ad, gets other ads posted by the same user: Also, can you make custom methods that return an object then use them in a chain? Code: // In the Author class: Greg
[eluser]stensi[/eluser]
I can change back to the default behaviour in the currently released version, that it auto-populates the related objects if they're not already populated. If already, populated it returns the existing data in it. It's going to be very difficult to allow both ways though. The reason I was changing it so you have to manually populate it was so it wouldn't automatically fill with all records. The best I think I can do is limit the auto-populate some how, maybe with just 1 record? ... If I get it working that way, it'll make it easier for me in terms of the documentation since the existing code will still work. The new version on its way will then basically just allow you to fine tune your related objects population if desired, otherwise they work like they currently do. I don't think I'll be able to chain custom objects like your last snippet there. I'll have a look into it though, but I expect that's going to be a bit tricky to pull off. For now, the stuff I'm finishing off in order are: - Better loading/handling of Related Objects. - Allow validation of non-Database Table fields and allow custom field labels for all error messages. - Manual Transactions for InnoDB or BDB Database Tables (if I have time, give the option to automate the transactions).
[eluser]GregX999[/eluser]
The "->all->get()" works well enough for me. I was just thinking of have the "->all->get()" be the default behavior for a relation that has yet to be populated. So that: $a->book Would be the same as: $a->book->all->get(); But you could still do stuff like: $a->book->where('pages > 100')->get(); or $a->books; But that's not important. I just thought it might be a neat thing. Which reminds me of something I forgot earlier... It would be nice if you could name relationships so that you could use $a->books instead of $a->book. Or if you had a Classified_Ad model you could use $member->ads instead of $member->classified_ad. Oh, and so models can reference themselves. For example, a "Employee" could have one "Supervisor" and have many "Underlings", both of which are also Employees. Greg
[eluser]stensi[/eluser]
Just for clarification, it's the other way round (related_object->get()->all) if you want to populate then access the all array. The reason I was changing it to not auto-populate by default, was because of memory concerns, such as it auto-populating with 100,000 records when the developer most likely wont need that many, so forcing them to choose to get all or narrow it down is better for them. Still, if they don't populate it, I'll see if I can default it to populate with all, when you try and access a property. Can't make any promises that I'll be able to pull that off. Not having much luck so far UPDATE Still not having luck with the auto-populate if not populated thing. Mainly because of the chaining, since when a __get kicks off from when a related object is accessed, I have no way of telling if the next part of the chain is ->all, which if its empty I should auto-populate, or if it's a ->where or ->get() etc, which means an auto-populate is not needed since the developer is doing one. I might have to just add a boolean setting that all related objects are auto-populated on first access if set to TRUE. If FALSE, you have to manually populate it yourself (better performance wise if you've got lots of records and don't want them all loaded). I'll be defaulting to FALSE. ______________________ Sorry but I wont be changing it to allow different names for the relationships. The reason it's the singular is because accessing $a->book is accessing the first book object and $a->book->all is an array of all the book objects returned. This is how it should be for the DataMapper pattern, and keeping things similar to the usage shown at DataMapper.org. Code: // Get first author object With the self referencing thing, I haven't tried that yet but my first thought would be that you'd have to have a Model for employees who have a One to Many relationship with other employees (supervisor has multiple employees to supervise), and a Model for employees who have a One to One with other employees (employees have one supervisor). This would require you have a joining table of "employees_employees". Supervisor Model Code: <?php Underling Model Code: <?php I haven't tested that but I'm hoping it will work, lol :-) Probably won't since I suspect it will be looking for the same join key of (employee_id) and which you obviously can't have two of. UPDATE Version 1.3 has been released! View the Change Log to see what's changed. In short, the related objects has had an overhaul and automatic population of the related objects is now off by default (you now populate them in much the same way you do the normal objects) but can be turned back on easily if desired. Validation has been improved, with the ability to properly label fields for use in error messages and you can now validate non-Database Table fields.
[eluser]GregX999[/eluser]
Great work on getting 1.3 out Stensi! If "self-relational" joins they wouldn't work due to both "id" fields being named the same, I think that's a big issue. I think they are an important thing to be able to use (I certainly use them in many projects - such as assigning "related products" to products in an e-commerce site). Also, Following the supervisor/underling example, what would you do for an employee that has both a supervisor and underlings? Or in the case of a social networking site, how would you handle having "friends" (since each friend needs to point to the other)? Maybe the reference "id" names in the join table can be named after the model instead of the database table - so you'd have supervisor_id and underling_id instead of two employee_id fields?? You explanation for wanting to use "book" (and "book->all") instead of "books" makes total sense. Greg
[eluser]stensi[/eluser]
Thanks Greg :-) Actually, now that you mention it you're right! It does use the model names to figure out the id fields in the joining tables. So the tables for the above self referencing example would be: employees id name employees_employees id supervisor_id underling_id The only problem, now that I've tested it, is that the JOIN query fails with the below error due to the JOIN having the employees table on both sides: Code: Not unique table/alias: 'employees' I'll see if I can figure this out. Hopefully it's just a matter of having a different JOIN query if it looks like it's going to be a self reference. If I get this working, I'll look at getting it to work for that 3rd type of employee scenario you mentioned. UPDATE Yep! It just needed the different type of JOIN query for a self reference :-) I'll post the fixed version for that sometime today. I'm testing out the 3rd type of employee and it's looking like I might need to change things around so it uses the pluralised version of the model for the table name, rather than just the table name, for joining tables. So the joining table between Supervisors and Underlings would change from employees_employees to become: supervisors_underlings id supervisor_id underling_id This would make it easier when you have more than two types of employees self referencing, such as a Manager, who would then have a joining table for each type of employee it could join with, example: managers_underlings id manager_id underling_id managers_supervisors id manager_id supervisor_id If I go down this route, I can remove the need for developers having to specify the $table value for any of their models or in the $has_many and $has_one arrays, and I'll just make DataMapper use CodeIgniter's Inflector helper for automatically figuring out ALL of the singular/plural work. It's smart enough to know that "Countries" is the plural of "Country" but unfortunately it's not smart enough to know "People" is the plural of "Person", and in those cases, this one particularly, you'd have to have "Person" as your Model and "Persons" as your Table. |
Welcome Guest, Not a member yet? Register Sign In |