Upgraded to L5 and loving it! However currently facing an issue with polymorphing a relationship. In my userable column in the Users table I need to include the namespaced value (e.g. "App\Employee" rather than just "Employee") for the polymorph relationship to work.
Feels like I am missing something.. How do I avoid forcing to store namespaced values in the table? :)
PS: Well aware others are struggling with similar issues (e.g. Laravel 5 namespaces) but have not seen anything relating to avoiding to store the namespacing in the tables..
BaseController
<?php namespace App\Http\Controllers;
use App\Client;
use App\User;
...
HomeController
$user = User::with('userable')->get();
foreach ($user as $u){
var_dump($u->userable->mobile);
}
User model
public function userable()
{
return $this->morphTo();
}
Client model
public function user()
{
return $this->morphOne('App\User', 'userable');
}
Employee model
public function user()
{
return $this->morphOne('App\User', 'userable');
}
Ended up creating a separate column in the User table. One for role (e.g. Admin, Employee etc.) and another column for the userable_type (e.g. '\App\Admin')
protected $morphClass
Seemed promising
(Polymorphic Eloquent relationships with namespaces) but has known bugs in the inversed relationship (Laravel 5 namespaces)
And even the latter does not work quite elegantly - so until it is resolved, I'll go for the two column approach - hopes this helps others!
Related
It is my first time to use yii and unlike my old programming style, i notice that it use relationship automatically in its model.
public function relations()
{
return array(
'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
'categories'=>array(self::MANY_MANY, 'Category',
'tbl_post_category(post_id, category_id)'),
);
}
I'm not used in doing this MySQL relationship. my old programming habit is connecting/manipulating the data to the php program itself.. To clarify my question, is this yii model relationship important? if i dont use this method, will i encounter problems?
Yii relations are very useful and if you work with it you will see that it will make you do less coding and make your code more readable.
while it is so much used in Yii applications, if you don't use relations, you won't get into any trouble, it is supposed to help you code and develop faster.
like if you looked at Yii blog, you have relation between Post model and Comments model, and you could go like this:
$post = Post::model()->findByPk( $id ); // find one post
$allCommentsRelated = $post->comments; // just one line for all search query and instanciating models
BTW in relations, there are two type of loading:
lazy loading (this is default mechanism)
eager loading
you have to know your scenario, and choose one that suites that scenario best
I'm new to MVC 3 and Entity Framework so I'd like to know what is the best approach.
Basically, I have 3 entities: Course, Module and Chapter. Each is a parent of the next with a one to many relationship (A Course has many Modules and a Module has many Chapters). I have a column SortOrder for Modules and Chapters to have them ordered sequentially.
My idea was is to use partial views for the child entities when updating the parent.
I have 3 views in mind:
Create/Update Course: all basic details for a course
Course Modules (basically a different view for Update Course) which has an option to add multiple partial views, each creating a Module
Course Timeline (still a different view for update course) which lists all Modules (on separate divs) and has the option to add multiple partial views, each creating a Chapter
Does my plan sound right and plausible? I plan to use hidden fields to store IDs. I also want the saves to occur asynchronously.
Any piece of advise or information would be highly appreciated. Thanks!
I think this is what your after but not sure. For handling persistence of child/grandchild entities, you can do this in several ways. You can either perform crud operations on each entity separately. So that will involve for example saving the modules by themselves with a reference to the course, probably courseId.
Or you can look at saving just the aggregate root, which in this case looks like its your Course entity. This will involve Loading the course, populating the modules on the course, and for each module populate the chapters. Then when you `db.Courses.Add(newCourse); db.SaveChanges(); all the entities will be persisted. You have to make sure your foreign key and model references are setup correctly.
For example, to save child entities:
public ActionResult DoSomething(int courseId, Module newModule)
{
var course = someService.LoadCourse(courseId);
course.Modules.Add(newModule);
using (var db = new MyDbContext())
{
db.Courses.Add(course);
db.SaveChanges();
}
return RedirectToAction("Success");
}
Or you can save individually:
public ActionResult DoSomething(Module newModule)
{
using (var db = new MyDbContext())
{
//You will need to make sure newModule.CourseId is set correctly
db.Modules.Add(newModule);
db.SaveChanges();
}
return RedirectToAction("Success");
}
Depending on your views, you will be able to judge which way is best to go. Regarding asynchronous saving, you will be able to call these endpoints with jquery posting the models as json. On a side note, one thing to look at would be to create a custom Anti Forgery Token validator for json requests, example.
In SQL, I have a 1:1 relationship defined between 2 tables which are linked by 2 mapping tables, four in total. I have no influence on the database schema.
I'd like to reflect this in my Code First model so that I can say Foo.Bar and Bar.Foo rather than Foo.Mapping1.Mapping2.Bar (or similar). Is this possible using the Fluent API? I know you can specify a many to many relationship using the designer which results in Foo.Bars and Bar.Foos so hopefully this is possible.
I don't know that you can map it with Fluent API but I know you can create an extension class and create an extension to handle the mapping like so:
public static class FooExtension
{
public static Bar Bar(this Foo)
{
var bar = Foo.Mapping1.Mapping2.Bar;
return bar;
}
}
Then you would call the extension method
var foosBar = Foo.Bar()
I asked a similar question a while back: Using the Data Mapper Pattern, Should the Entities (Domain Objects) know about the Mapper? However, it was generic and I'm really interested in how to accomplish a few things with Doctrine2 specifically.
Here's a simple example model: Each Thing can have a Vote from a User, a User may cast more than one Vote but only the last Vote counts. Because other data (Msssage, etc) is related to the Vote, when the second Vote is placed the original Vote can't just be updated, it needs to be replaced.
Currently Thing has this function:
public function addVote($vote)
{
$vote->entity = $this;
}
And Vote takes care of setting up the relationship:
public function setThing(Model_Thing $thing)
{
$this->thing = $thing;
$thing->votes[] = $this;
}
It seems to me that ensuring a User only has the last Vote counted is something the Thing should ensure, and not some service layer.
So to keep that in the Model, the new Thing function:
public function addVote($vote)
{
foreach($this->votes as $v){
if($v->user === $vote->user){
//remove vote
}
}
$vote->entity = $this;
}
So how do I remove the Vote from within the Domain Model? Should I relax Vote::setThing() to accept a NULL? Should I involve some kind of service layer that Thing can use to remove the vote? Once the votes start accumulating, that foreach is going to be slow - should a service layer be used to allow Thing to search for a Vote without having to load the entire collection?
I'm definitely leaning toward using a light service layer; however, is there a better way to handle this type of thing with Doctrine2, or am I heading in the right direction?
I vote for the service layer. I've often struggled with trying to add as much logic on the Entity itself, and simply frustrated myself. Without access to the EntityManager, you're simply not able to perform query logic, and you'll find yourself using a lot of O(n) operations or lazy-loading entire relationship sets when you only need a few records (which is super lame when compared to all the advantages DQL offers).
If you need some assistance getting over the idea that the Anemic Domain Model is always an anti-pattern, see this presentation by Matthew Weier O'Phinney or this question.
And while I could be misinterpreting the terminology, I'm not completely convinced that Entities have to be the only objects allowed in your Domain Model. I would easily consider that the sum of Entity objects and their Services constitutes the Model. I think the anti-pattern arises when you end up writing a service layer that pays little to no attention to separation of concerns.
I've often flirted with the idea of having all my entity objects proxy some methods to the service layer:
public function addVote($vote)
{
$this->_service->addVoteToThing($vote, $thing);
}
However, since Doctrine does not have any kind callback event system on object hydration, I haven't found an elegant way to inject the service object.
My advice would be to put all the query logic into an EntityRepository and then make an interface out of it sort of like:
class BlogPostRepository extends EntityRepository implements IBlogPostRepository {}
that way you can use the interface in your unit-tests for the service objects and no dependency on the EntityManager is required.
I am very interested in Linq to SQL with Lazy load feature. And in my project I used AutoMapper to map DB Model to Domain Model (from DB_RoleInfo to DO_RoleInfo). In my repository code as below:
public DO_RoleInfo SelectByKey(Guid Key)
{
return SelectAll().Where(x => x.Id == Key).SingleOrDefault();
}
public IQueryable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
SelectAll method is run well, but when I call SelectByKey, I get the error:
Method “RealMVC.Data.DO_RoleInfo MapDB_RoleInfo,DO_RoleInfo” could not translate to SQL.
Is it that Automapper doesn't support Linq completely?
Instead of Automapper, I tried the manual mapping code below:
public IQueryable<DO_RoleInfo> SelectAll()
{
return from role in _ctx.DB_RoleInfo
select new DO_RoleInfo
{
Id = role.id,
name = role.name,
code = role.code
};
}
This method works the way I want it to.
While #Aaronaught's answer was correct at the time of writing, as often the world has changed and AutoMapper with it. In the mean time, QueryableExtensions were added to the code base which added support for projections that get translated into expressions and, finally, SQL.
The core extension method is ProjectTo1. This is what your code could look like:
using AutoMapper.QueryableExtensions;
public IQueryable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return _ctx.DB_RoleInfo.ProjectTo<DO_RoleInfo>();
}
and it would behave like the manual mapping. (The CreateMap statement is here for demonstration purposes. Normally, you'd define mappings once at application startup).
Thus, only the columns that are required for the mapping are queried and the result is an IQueryable that still has the original query provider (linq-to-sql, linq-to-entities, whatever). So it is still composable and this will translate into a WHERE clause in SQL:
SelectAll().Where(x => x.Id == Key).SingleOrDefault();
1 Project().To<T>() prior to v. 4.1.0
Change your second function to this:
public IEnumerable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo.ToList()
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
AutoMapper works just fine with Linq to SQL, but it can't be executed as part of the deferred query. Adding ToList() at the end of your Linq query causes it to immediately evaluate the results, instead of trying to translate the AutoMapper segment as part of the query.
Clarification
The notion of deferred execution (not "lazy load") does not make any sense once you've changed the resulting type to something that's not a data entity. Consider these two classes:
public class DB_RoleInfo
{
public int ID { get; set; }
public string Name { get; set; }
}
public class DO_RoleInfo
{
public Role Role { get; set; } // Enumeration type
}
Now consider the following mapping:
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>
.ForMember(dest => dest.Role, opt => opt.MapFrom(src =>
(Role)Enum.Parse(typeof(Role), src.Name)));
This mapping is completely fine (unless I made a typo), but let's say you write the SelectAll method in your original post instead of my revised one:
public IQueryable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
This actually kind of works, but by calling itself a "queryable", it lies. What happens if I try to write this against it:
public IEnumerable<DO_RoleInfo> SelectSome()
{
return from ri in SelectAll()
where (ri.Role == Role.Administrator) ||
(ri.Role == Role.Executive)
select ri;
}
Think really hard about this. How could Linq to SQL possibly be able to successfully turn your where into an actual database query?
Linq knows nothing about the DO_RoleInfo class. It doesn't know how to do the mapping backward - in some cases, that may not even possible. Sure, you may look at this code and go "Oh, that's easy, just search for 'Administrator' or 'Executive' in the Name column", but you're the only one who knows that. As far as Linq to SQL is concerned, the query is pure nonsense.
Imagine that somebody gave you these instructions:
Go to the supermarket and bring back the ingredients for making Morton Thompson Turkey.
Unless you've made it before, and most people haven't, your response to that instruction is most likely going to be:
What the hell is that?
You can go to the market, and you can get specific ingredients by name, but you can't evaluate the condition I've given you while you're over there. I have to "un-map" the criteria first. I have to tell you, here are the ingredients we need for this recipe - now go and get them.
To summarize, this is not some simple incompatibility between Linq to SQL and AutoMapper. It is not unique to either of those two libraries. It doesn't matter how you actually do the mapping to a non-entity type - you could just as easily do the mapping manually, and you'd still get the same error, because you are now giving Linq to SQL a set of instructions that are no longer comprehensible, dealing with mysterious classes that don't have an intrinsic mapping to any particular entity type.
This issue is fundamental to the concept of O/R Mapping and deferred query execution. A projection is a one-way operation. Once you project, you can no longer go back to the query engine and say oh by the way, here are some more conditions for you. It's too late. The best you can do is take what it already gave you and evaluate the extra conditions yourself.
Last but not least, I'll leave you with a workaround. If the only thing you want to be able to do from your mapping is filter the rows, you can write this:
public IEnumerable<DO_RoleInfo> SelectRoles(Func<DB_RoleInfo, bool> selector)
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return _ctx.DB_RoleInfo
.Where(selector)
.Select(dbr => Mapper.Map<DB_RoleInfo, DO_RoleInfo>(dbr));
}
This is a utility method that handles the mapping for you and accepts a filter on the original entity, and not the mapped entity. It might be useful if you have many different kinds of filters but always need to do the same mapping.
Personally, I think you will be better off just writing out the queries properly, by first determining what you need to retrieve from the database, then doing any projections/mappings, and then, finally, if you need to do further filtering (which you shouldn't), then materialize the results with ToList() or ToArray() and write more conditions against the local list.
Don't try to use AutoMapper or any other tool to hide the real entities exposed by Linq to SQL. The domain model is your public interface. The queries you write are an aspect of your private implementation. It's important to understand the difference and maintain a good separation of concerns.