I have the following code in PostController of my Symfony2 application
$dm = $this->get('doctrine_mongodb')->getManager();
$fields = array('id','title', 'content');
$qb = $dm->createQueryBuilder('AppBundle:BlogPost')
->select($fields);
$query = $qb->getQuery();
$blogPosts = $query->execute();
return $this->render('post/list.html.twig',array('posts' => $blogPosts));
And the following in it's view
<ol id="navigation">
{% for post in posts %}
<li> {{ post.id }} - {{ post.content }} delete </li>
{% endfor %}
</ol>
However, the id field returned to the view is always blank. I tried clearing the cache many times but still the id is blank. Am I doing anything wrong here?
Answering my own question.
It was pretty simple. I didn't have setter/getter for id field in AppBundle:BlogPost entity. Apparently, Doctrine Mongodb uses the getter/setter to populate values from the the db.
UPDATE: It's not Doctrine but Twig template engine which requires the getter/setter.
/** #ODM\Id */
protected $id;
public function setId($valId){
$this->id=$valId;
}
public function getId(){
return $this->id;
}
Related
Scenario
What i try to do
I am creating a multicolumn user index page, where the right column shows details from the user selected in the left column.
When selected, the user is not pulled out of the collection but freshly out of the database, so the data is up to date.
I defer the loading of the user list using the described method in the livewire documentation.
The user has a 'roles' relationship, which is displayed in the list column.
What I'd expect
I would expect that once the $this→users is set as a collection of the users and a user is selected, only the query will fire for getting the data for this user.
What actually happens
When a user is selected, a query for getting all users from the database is run (again), and because of the fact that the roles from the user are displayed in the list view, for each user, a new query is executed.
After that, a query for getting the selected user is executed. Afterwards another query for getting the roles of the user is fired to.
So my questions
Why does Livewire lose the relations that were eager loaded in the first declaration of public $users?
Why is it that Livewire reruns the query for getting all users, while the public $users is already defined as a collection of users?
Files:
UserListDetail.php
<?php
namespace App\Http\Livewire;
use App\Models\User;
use Livewire\Component;
class UsersListDetail extends Component {
public string $search = '';
public $users;
public $selectedUser;
public int $timesRun = 0;
public bool $readyToLoadUserList = false;
protected $queryString = [
'search' => [ 'except' => '' ],
];
// Defer loading users
public function readyToLoadUserList()
{
// Get all users with roles relationship
$this->users = User::with('roles')->get();
$this->readyToLoadUserList = true;
}
public function selectUser(int $userId)
{
$this->selectedUser = User::with('roles')->find($userId);
}
public function render()
{
return view('livewire.users-list-detail', [
'selectedUser' => $this->selectedUser,
]
);
}
}
simplified version of user-list-detail.blade.php
<div>
<div wire:init="readyToLoadUserList">
#if($readyToLoadUserList)
<ul>
#foreach($users as $user)
<li wire:click="selectUser({{ $user->id }})">
{{ $user→name_first }} {{ $user→name_last }},
#foreach($user→roles as $role)
{{ $role→label }},
#endforeach
</li>
#endforeach
</ul>
#endif
</div>
<div>
#isset($selectedUser)
{{ $name_first
#endisset
</div>
</div>
When selectUser() method is triggered, the livewire will re-render the blade and since wire:init="readyToLoadUserList" is there, it will load every user (again).
Replce readyToLoadUserList() with mount() and simply keep wire:init="" empty.
Also, condition with #if($users->count() > 0)
I'm developing a personal blog but I can't get the browser to show me what I have in a function which has yet to show me a single post, however I can manage to view a list of all the posts, I can view just 5 (latest) which appear on the main, homepage.
Now I'm going for the part where you click on the title and everything about this post should be displayed. I inspected the code on the part where I'm having the issues and the part where the foreach is isn't being rendered to the browser, maybe I'm not doing something correct on the blade part?
PostsController.php
public function show(Post $slug)
{
$post = Post::find($slug);
return view('posts.show', ['posts' => $post]);
}
Posts/show.blade.php
#extends('layouts.app')
#section('showpost')
<div>
#foreach ($posts as $post)
<h1> {{ $post->title }} </h1>
<p> {{ $post->body }} </p>
#endforeach
</div>
#endsection
web.php
Route::get('/posts/{post}', 'PostsController#show');
Just to show you how I'm referencing the template that shows just one post.
#foreach ($posts as $post)
<li>
<a href="/posts/{{ $post->slug }}">
<h1> {{ $post->title }} </h1>
</a>
<p> {{ $post->slug }} </p>
</li>
#endforeach
Post::find is used to retrieve a model by its primary key which is in most cases named id. Heir in your case you are passing $slug as an argument, and as It's is supposed to render only a single item with a given slug you should use Post::where('slug', $slug)->first() this will only return one item and you won't have to use a foreach loop in the show.blade.php template.
Another problem is here
public function show(Post $slug) {
$post = Post::find($slug);
return view('posts.show', ['posts' => $post]);
}
You are using Route Model Binding which will automatically set the parameter $slug of the function show equal to the record in the posts table which has the id which is passed in the URL request. No need to perform Post::find($slug)
for example, if the request URL is http://localhost:8000/posts/1 here the id is 1 so the parameter $slug will be populated with the record which has id==1 unless you have Customizing The Key like this
Define in the App\Models\Post::class a method call
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug';
}
With this definition, the post item which will be retrieved when performing Route model biding will be based on the value of the slug column within a posts table.
You can learn more about at Route Model Biding
In order to implement the Route Model Binding completely, you need to specify it in 3 places, routes.php, PostsController.php and override it on the model, Post.php:
Pass the desired target inside the get request method and place it inside the wildcard, this is next to where you specify the controller and which function to use inside of the controller.
From this: Route::get('/posts/{post}', 'PostsController#show');
To this: Route::get('/posts/{slug}', 'PostsController#show');
Override the target (from id to "whatever") using the getRouteKeyModel function, this is just like #Yves Kipondo had provided
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug';
}
Tweak the show function inside the PostsController, passing the Model's name and the target so the query is completed. All of this inside the parameter of the function. And since your'e overriding the use of the id, you no longer have to specify it inside Eloquent query to the database.
So, instead of this:
public function show(Post $slug)
{
$post = Post::find($slug);
return view('posts.show', ['posts' => $post]);
}
Change it to this:
public function show(Post $slug)
{
return view('posts.show', ['post' => $slug]);
}
I have three models and have to follow the hierarchy (Module > Chapter > Lesson)
I'm trying to make a loop that will get all from the database and print out accordingly
#foreach($modules as $module)
<h4>{{ $module->title }}</h4>
<div id="chapter_bar" style="padding-left: 30px;">
#foreach($module->chapters as $chapter)
<h5>{{ $chapter->title }}</h5>
<ul>
#foreach($chapter->lessons as $lesson)
<li>{{ $lesson->title }}</li>
#endforeach
</ul>
#endforeach
</div>
#endforeach
Everything works fine until the lessons are printed out, since in each module chapters' ids reset from 1. Chapters are printed in the correct module, but their lessons are not.
Modules > Chapters > Lessons List
Data Structure in DB
Model relationships:
Module
public function chapters(){
return $this->hasMany('App\Chapter');
}
public function lessons(){
return $this->hasMany('App\Lesson');
}
Chapter
public function lessons(){
return $this->hasMany('App\Lesson');
}
public function module(){
return $this->belongsTo('App\Module');
}
Lesson
public function chapter(){
return $this->belongsTo('App\Chapter');
}
public function module(){
return $this->belongsTo('App\Module');
}
To my understanding when you are doing $chapter->lessons it's returning all the lessons (with id: 1,2,4,5) from the db. It's because the relation you set in the chapter model:
// Chapter model
public function lessons(){
return $this->hasMany('App\Lesson');
}
Which is only checking the relation between chapter id and lesson id. But you need the module id checking as well.
I don't know any better way to do this but you can do something like following to add the module id filter:
// Chapter model
public function lessons(){
return $this->hasMany('App\Lesson')->where('module_id', $this->module_id)->all();
}
aka: give me all the lessons of this chapter where their module id is same as the chapter's module id.
I hope you get the point. Hope this helps or at least give you some idea!
I have created the front end relationship between my models image and album by using hasMany and belongsTo however I am not getting any results displayed and
{{ dump(images) }} and {{ dump(albums) }} gives Object variables NULL 0 NULL
Any Ideas why the relationship is not being picked up?
Album
public $hasMany = [
'images' => [ 'Acme\Library\Models\Image' ,'key'=>'album_id'],
];
Image
public $belongsTo = [
'albums' => ['Acme\Library\Models\Album']
];
album-details.htm
{{ dump(images) }}
{% for image in images %}
{{image.id|raw }}
{% endfor %}
if I run a dump on {{ dump(record) }} I get
Object variables
Acme\Library\Models\Album
bootValidation() method Boot the validation trait for this model.
forceSave() method Force save the model even if validation fails.
validate() method Validate the model instance
isAttributeRequired() method Determines if an attribute is required based on the validation rules.
errors() method Get validation error message collection for the Model
validating() method Create a new native event for handling beforeValidate().
validated() method Create a new native event for handling afterValidate().
rules array(0)
timestamps boolean
table string
relationConfig string
hasMany array(1)
I can see using the builder plugin that record is defined {% set records = builderList.records %}
Does albums/images need to be defined in this manner also?
I you send record variable to the view and record is an instance of Album you can access your images this way.
{% for image in record.images %}
{{image.id|raw }}
{% endfor %}
I have a user entity and a log entity. I have a OneToMany connection from User to Log.
In my Log Table are stored log entries according to users.
I output a User list:
Controller:
$user = $this->getDoctrine()
->getRepository('SeotoolMainBundle:User')
->findBy(array('isActive' => 1, 'isAdmin' => 0));
TWIG
{% for user in list_user %}
{{ user.username }}
{% endfor %}
Now I want to recieve ONE row of the log table, sorted by a field called "date" and return it in the for user loop like this:
{% for user in list_user %}
{{ user.username }}
{{ user.log.lastEntry
{% endfor %}
How do I do this?
Entity User.php:
/**
* #ORM\OneToMany(targetEntity="Log", mappedBy="user")
* #ORM\OneToMany(targetEntity="Log", mappedBy="editor")
*/
protected $log;
Entity Log.php
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="log")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
protected $User;
Assuming that your getter for accessing the Log Collection object is a getLogMessages method, and assuming you can access the log message with a getMessage method, the following should solve it:
{{ User.getLogMessages().last().getMessage() }}
The last method gives you access to the last element stored in the collection.
By the way, you should use the OrderBy annotation, otherwise the order must be considered undefined. (Although, in reality, you will usually get the elements in the order they were inserted).
I solved it now in TWIG. But this isn't nice at all... I would prefere a solution inside of the controller and not in the VIEW.
{% for log in last_userlog|reverse if(log.user.Id == user.Id) %}
{% if loop.index == 1 %}
{{ log.logTitle }}
{% endif %}
{% endfor %}