I’m using Slim with Eloquent URL and have stumbled across and issue I have been unable to resolve so far. Apologies if this is not the right place to ask Eloquent questions:
I have DB relationship which is set up as follows:
Topic <- Item <- User
To clarify:
- A Topic has many items
- Each item is associated to a user (via user_id)
I have modeled this on the ORM level like this:
Topic:
$this->hasMany('\\App\\Model\\Comment');
Comment:
$this->belongsTo ('\\App\\Model\\Topic');
$this->belongsTo ('\\App\\Model\\User');
User:
$this->hasMany ('Comment');
I have 2 questions regarding this:
-
In a Twig template, I’m trying to do
{{ comment.user.username }}
but that does not seem to be working. So this brings me to the question: how can I access Eloquent relations from within twig?
- The above code, even if it would be working, would fetch each user record for each comment. Strictly speaking this is a waste because I’m only interested in the ‘username’ field, there is no need to fetch each user record. I’d rather solve this via a left-join when comments are fetched. However, I’ve been unable to figure out how to trick Eloquent into using a left-join when getting the relation info (ie: $topic->comment). My guess would be that I’d have to override a method somewhere, but I have no idea where.
As usual, any hints are highly appreciated.
Can you post the code where you pass the comment object to the template?
Certainly. This is the actual template invocation:
return $this->view->render ($response, TemplateResolver::getTemplate($this->settings, 'helpdesk_topic'), [
'topic' => $topic,
'comments' => $topic->comments,
'pageName' => _('Helpdesk Followup')
]);
TemplateResolver simply maps the name of a route to a template.
For completeness sake, comments are processed in the twig template like this:
{% for comment in comments %}
<tr>
<td>
<span class="bold">{{ comment.cr_date }}</span> by <span class="bold"> {{ comment.user.username }}</span>
<br />
{{ comment.content|nl2br }}
</td>
</tr>
{% endfor %}
Appreciate your help.
Nice, thanks. In order for something like {{ comment.user.username }}
to work, username
needs to be either a property of the user object, or a method on the user object. If you var_dump a comment
in your loop do they have a user and then username property
? Is {{ comment.cr_date }}
is working properly?
comment.cr date is working. But dumping the info points towards a culprit (although I have no idea why):
class App\Model\HelpdeskComment#151 (23) { protected $table => string(16) “helpdesk_comment” protected $guarded => array(2) { [0] => string(2) “id” [1] => string(17) “helpdesk_topic_id” } public $timestamps => bool(false) protected $connection => NULL protected $primaryKey => string(2) “id” protected $perPage => int(15) public $incrementing => bool(true) protected $attributes => array(7) { ‘id’ => string(1) “9” ‘content’ => string(3) “xxx” ‘file_attachment’ => string(0) “” ‘status’ => string(1) “O” ‘cr_date’ => string(19) “2016-05-02 23:38:40” ‘helpdesk_topic_id’ => string(1) “3” ‘user_id’ => string(1) “1” } protected $original => array(7) { ‘id’ => string(1) “9” ‘content’ => string(3) “xxx” ‘file_attachment’ => string(0) “” ‘status’ => string(1) “O” ‘cr_date’ => string(19) “2016-05-02 23:38:40” ‘helpdesk_topic_id’ => string(1) “3” ‘user_id’ => string(1) “1” } protected $relations => array(0) { } protected $hidden => array(0) { } protected $visible => array(0) { } protected $appends => array(0) { } protected $fillable => array(0) { } protected $dates => array(0) { } protected $dateFormat => NULL protected $casts => array(0) { } protected $touches => array(0) { } protected $observables => array(0) { } protected $with => array(0) { } protected $morphClass => NULL public $exists => bool(true) public $wasRecentlyCreated => bool(false) }
Note the
protected $relations => array(0)
My PHP Model files look like this:
class BaseModel extends Model
{
protected $guarded = ['id']; // All our tables have a primary key named "id"
public $timestamps = false; // We let MYSQL column defaults handle these
}
class HelpdeskTopic extends BaseModel
{
protected $table = 'helpdesk_topic';
protected $guarded = ['id', 'user_id', 'wallet_id'];
public function comments () {
return $this->hasMany('\\App\\Model\\HelpdeskComment');
}
public function user () {
return $this->belongsTo ('\\App\\Model\\User');
}
}
class HelpdeskComment extends BaseModel
{
protected $table = 'helpdesk_comment';
protected $guarded = ['id', 'helpdesk_topic_id'];
public function topic () {
return $this->belongsTo ('\\App\\Model\\HelpdeskTopic');
}
public function user () {
return $this->belongsTo ('\\App\\Model\\User');
}
}
I have the feeling that it’s some “duh” (ie: totally stupid) mistake, but I’m just not seeing it. Thanks in advance for any insights.
I’m not seeing it either.
Yeah, so far this has eluded me. I may just do it on the PHP side and pass the results to Twig so it can display them. Thanks for looking though.
Check your database to make sure that those comments actually have a user assigned. As per your question to the leftJoin, you should be able to create a method in your HelpdeskComment
model to do the join and return data.
class HelpdeskComment extends BaseModel
{
public function username()
{
return $this->leftJoin('user', 'user.id', '=', 'helpdesk_comment_user_id')->value('username');
}
}
The code above should work, but I haven’t tested it and I haven’t used Eloquent in a while.
I had the same issue and apparentyl the relation does not get reoslved in Twig unless you access the data directly in PHP. I encountered this when I was doing something similar to you.
1 Like
Thank your for this piece of info. I resolved the issue at the PHP level but I did not know about this twig limitation so I went with the only thing which seemed to work.
Is there currently any way to fix this at the Twig level, or do we have to use the workaround silentworks showed?
Cannot test it currently since travelling but maybe eager loading solves this issue? See https://laravel.com/docs/5.1/eloquent-relationships#eager-loading
There also is a performance issue (the exact performance characteristics of course depend on your data and use case): Using the eloquent relation resolution, you are performing a select on every item to fetch it’s related items. It may be fast to just fetch the relation data you need (especially when it’s only a single field) using a left join.