问题
I've been at this for 5 hours now and can't seem to figure out the mistake. I'm using Polymorphic Many-to-Many Relations in Laravel 4.1.
A Job and an Event each can have a Tag and I've set up the stuff accordingly. But whenever I call
$job->tags
it returns empty.
I have these 3 classes (BaseModel extends Eloquent, namespace is always App\Models, I have to reference them in the e.g. "morphToMany" function, doesn't work with "use"):
class Job extends BaseModel {
public function tags()
{
return $this->morphToMany('\App\Models\Tag', 'taggable');
}
[...]
}
The Tag Model:
class Tag extends BaseModel {
public function jobs(){
return $this->morphedByMany('\App\Models\Job', 'taggable');
}
public function events(){
return $this->morphedByMany('\App\Models\Event', 'taggable');
}
[...]
}
The Event Model ("event" as in conference, seminar - has own namespace App\Models to avoid conflict):
class Event extends BaseModel {
[... nothing relevant yet ...]
}
In my JobsController I have (test case, job with ID 14 exists)
public function show($slug)
{
$job = Job::find(14);
return \View::make('jobs.show', ['job' => $job]);
}
and my app/views/jobs/show.blade.php
[...]
echo $job->name;
echo $job->company;
foreach($job->tags as $tag) {
echo $tag->name;
}
[...]
The first outputs work just fine and it show the $job->name and $job->company correctly, so it's reading from the jobs table correctly, but
$job->tags
returns empty AND $tag->name is never called. I have a tags and taggable table (MySQL), here are the relevant lines
taggables (table)
-> id->increments()
-> tag_id->integer()->unsigned()->index
->foreign('tag_id')->references('id')->on('tag')
-> taggable_id->integer()->unsigned()->index()
-> taggable_type->string()->index()
tags (table)
-> id->increments()
-> name->string()
-> slug->string()
Test 1
When I do
$jobs = Job::has('tags')->get();
in my JobsController.php view it actually only returns the jobs which have tags, so I'm a bit hopeful that it works a little bit.
Test 2
But when I try to get the tags e.g. in this index.blade.php case
foreach($jobs as $job){
foreach($job->tags as $tag){
echo $tag->name;
}
}
it goes into the $jobs loop just fine, but it doesn't go into the $job->tags loop. In my taggables table I have a dataset
taggables
id: 1
tag_id: 1 (exists)
taggable_id: 14 (via foreign key)
taggable_type: Job
I'm going nuts, can't figure out where the problem lies. Am I missing something?
回答1:
Cleaner Solution
I already always used a BaseModel which extends Eloquent. The Models then only extend BaseModel, so I can do some changes to Eloquent, so to say. I can easily then overwrite the Eloquent morphToMany() function, using the same content, with one tiny change:
<?php namespace App\Models;
class BaseModel extends \Illuminate\Database\Eloquent\Model {
public function morphToMany($related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
{
$caller = $this->getBelongsToManyCaller();
$foreignKey = $foreignKey ?: $name.'_id';
$instance = new $related;
$otherKey = $otherKey ?: $instance->getForeignKey();
$query = $instance->newQuery();
$table = $table ?: str_plural($name);
// Only Change: MyMorphToMany instead of the standard MorphToMany
return new \MyMorphToMany(
$query, $this, $name, $table, $foreignKey,
$otherKey, $caller, $inverse
);
}
[...]
}
Since the morphedByMany() function actually also calls morphToMany() there's no need to rewrite that for polymorphic many-to-many relations. I then went on to copy the whole of
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php
to
app/models/MyMorphToMany.php
with only a few changes:
<?
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphPivot;
// no namespace, instead call it when extending
// additionally use Illuminate\Database\Eloquent\Relations\MorphPivot;
// Class MyMorphToMany instead of MorphToMany
class MyMorphToMany extends Illuminate\Database\Eloquent\Relations\BelongsToMany {
[...]
public function __construct(Builder $query, Model $parent, $name, $table, $foreignKey, $otherKey, $relationName = null, $inverse = false)
{
$this->inverse = $inverse;
$this->morphType = $name.'_type';
$this->morphClass = $inverse ? get_class($query->getModel()) : get_class($parent);
// This is the change to cut everything after "\" in the namespaced Class
$this->morphClass = substr($this->morphClass, strrpos($this->morphClass, '\\') + 1);
parent::__construct($query, $parent, $table, $foreignKey, $otherKey, $relationName);
}
[...]
}
And that should do it, I think. You should now be able to use e.g. morphToMany('App\Models\Tag', 'taggable'); for a polymorphic many-to-many relation in Laravel now.
Thanks to https://stackoverflow.com/a/19884991/3347365 for the hint.
Previous Comment / Solution
That's weird. When I remove the namespaces, it works! I've removed the namespaces of Job and Tag class, so I can call via
public function tags(){
return $this->morphToMany('Tag', 'taggable');
}
in the Job Model instead of
public function tags(){
return $this->morphToMany('App\Models\Tag', 'taggable');
}
and it works! Is this a bug or did I implement something wrongly? I use PSR-4 mostly, but I'm not sure if that has anything to do with it.
来源:https://stackoverflow.com/questions/22254285/laravel-polymorphic-many-to-many-returns-empty