Unfortunately, I don't have that much experience with Eloquent yet. I try to create a query from three tables which have two pivot tables.
My tables:
My Models:
Player
class Player extends Model
{
protected $table = 'players';
protected $fillable = [
'name'
];
public function layout(){
return $this->belongsToMany('App\Layout', 'layout_player', 'player_id', 'layout_id');
}
public function information(){
return $this->hasMany('App\Information', 'player_id');
}
}
Layout
class Layout extends Model
{
protected $table = 'layouts';
protected $fillable = [
'name'
];
public function player(){
return $this->belongsToMany('App\Player', 'layout_player', 'layout_id', 'player_id');
}
public function item(){
return $this->belongsToMany('App\Item', 'item_layout', 'layout_id', 'item_id');
}
}
Item
class Item extends Model
{
protected $table = 'items';
protected $fillable = [
'name'
];
public function layout(){
//return $this->hasOne(Layout::class);
return $this->belongsToMany('App\Layout', 'item_layout', 'item_id', 'layout_id');
}
}
Starting from the player, I want to retrieve the current player, all layouts and the corresponding items. Unfortunately I can't do it.
I call up the player and layouts as follows:
Player::where('id',1)->with('layout')->get();
How do I additionally get all items in the query?
You made a relationship perfectly. Now from Player to layout you're getting it with('layout'). Try it.
$players = Player::with('layout.item')->where('id',1)->get();
It'll give you players along with layouts with items.
If I understand your question, I think you are almost there.
Add this to Player model too, like other methods you did.
public function contents(){
return $this->belongsToMany('App\Content');
}
To get all contents regarding a player write these in controller and pass it to view file.
$player = Player::findOrFail(1);
return view('path.to.file_name',compact('player'));
In view file
//get all contents of a player
#foreach($player->contents as $content)
<p>{{ $content->text}}</p>
#endforeach
//get all layouts of a player
#foreach($player->layout as $layout)
<p>{{ $layout->name}}</p>
#endforeach
//get all items of a player
#foreach($player->layout as $layout)
<p>{{ $layout->name}}</p>
#foreach($layout->item as $item)
<p>{{ $item->name }}</p>
#endforeach
#endforeach
Thank you very much for the quick answer. Unfortunately this does not solve my problem.
I call the PlayerController via the api route and need all objects of the player in the form as return:
player
layout
item
public function show($id)
{
$player = Player::findOrFail($id);
//$player = Player::where('id',$id)->with('layout')->get();
return $player;
}
I get this response:
{"id":1,"name":"Testplayer","created_at":"2019-09-22 15:53:07","updated_at":"2019-09-22 15:53:07"}
But I need also the layouts and Items.
I hope you still understand my bad English.;)
Related
I have a simple code, and now I want to select data from 2 tables. Then I want to show in my view. Here's my code
model:
Tikpro.php
class Tikpro extends Model {
public $table = "TIKPRO";
public $primaryKey = "ID_TIKPRO";
public function permintaan() {
return $this->hasMany('Permintaan', 'TIKPRO_ID', 'ID_TIKPRO');
}
model:
Permintaan.php
class Permintaan extends Model {
public $table = "PERMINTAAN";
public $fillable = array(
'NOMOR_TICKET',
'TGL_PERMINTAAN',
'NAMA_REQUESTER',
'BARANG_PERMINTAAN',
'NO_FPBJ',
'TGL_INPUT_FPBJ',
'TARGET_SELESAI',
'KETERANGAN',
'TINDAK_LANJUT_AKHIR',
'STATUS',
'FPB',
'RFQ',
'DO',
'BAST',
'TGL_DEADLINE',
'titik_proses',
'TIKPRO_ID',
);
public $primaryKey = "ID_PERMINTAAN";
public function tikpro() {
return $this->belongsTo('Tikpro','TIKPRO_ID','ID_TIKPRO');
}
Controller:
PermintaanController.php
public function details($ID_PERMINTAAN) {
$query = DB::table('PERMINTAAN')->select('TIKPRO.NAMA_TIKPRO')->join('TIKPRO','TIKPRO.ID_TIKPRO','=','PERMINTAAN.TIKPRO_ID')->get('all');
$jebret = Permintaan::find($ID_PERMINTAAN);
return view('permintaan.details', compact('jebret'))->with($query);
}
And how can I show it? In my view I try {{ $jebret->$query }} . But still can't show the data.
How should I write code? Thanks
I think you can try this
public function details($ID_PERMINTAAN) {
$query = DB::table('PERMINTAAN')->select('TIKPRO.NAMA_TIKPRO')->join('TIKPRO','TIKPRO.ID_TIKPRO','=','PERMINTAAN.TIKPRO_ID')->where('PERMINTAAN.ID_PERMINTAAN',$ID_PERMINTAAN)->get();
return view('permintaan.details', compact('query'));
}
Hope this work for you
There's 2 ways here. The first and easiest (if you've set it up correctly) is the relationship. https://laravel.com/docs/5.4/eloquent-relationships. To use the one in your example you want to do this:
$permintaans = Permintaan::query()->get()->all();
return view('permintaan.details', [
'permintaans' => $permintaans,
]);
//In the view
#foreach($permintaans as $permintaan)
<tr>{{$permintaan->tikpro->attribute}}</tr>
#endforeach
Another way like you've attempted is the join. In the select though I would leave it blank so you get all columns!
$permintaans = Permintaan::query()
->join('TIKPRO','TIKPRO.ID_TIKPRO','=','PERMINTAAN.TIKPRO_ID')
->get()
->all();
So, this is weird. I have been implementing ontoMany relationships between users and various data sets. The first one worked... sort of. I set up the pivot table and what not, the data is correct on both ends of the table but the result when laravel calls the data is not even close:
Let's take this show user data function:
public function show($id)
{
try {
$loc = UserEdit::findorFail($id);
$array = UserEdit::findorFail($id)->toArray();
//$prefs = Ministry_Prefs_User::find($id);
return view('UserEdit', compact('array', 'loc'));
} catch(\Exception $e) {
return \Redirect::route('users.index')
->withMessage('This user does not exist');
}
}
In the blade I print out the tags for their preferences:
Preferences:<br>
{{ $array['Preferences'] }}<br>
#foreach ($loc->prefs_user as $tag)
{{ $tag->tag }}<br>
#endforeach<br><br><br>
The first array prints what's stored in the original untouched user data from the old table I inherited. That's so I can compare and make sure I'm getting the right data from the pivot tables I had to generate from this info. Turns out that was a good idea, coz this is what's printing:
1A-4,1-2,1-3,2-3,3-6,4-7,6-11,8-6,8-10,9-4,7A-4
1A-1
1A-1
1A-1
1A-3
1A-4
1A-5
1A-7
1-1
1-1
1-6
1A-8
I can see no pattern other than alphabetical order? Why would this happen?
The next point is even weirder. Using the same code get's me no results at all from the other pivot tables I've set up:
public function country() {
return $this->belongsToMany('App\Country', 'country_user', 'user_id', 'country_id');
}
public function prefs_user() {
return $this->belongsToMany('App\Prefs_User', 'tag_user', 'user_id', 'tag_id');
}
public function language() {
return $this->belongsToMany('App\language', 'language_user', 'user_id', 'language_id');
Why would this happen? The models look like this:
Prefs_User
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Prefs_User extends Model
{
protected $table = 'prefs';
public function prefs_user() {
return $this->belongsToMany('App\UserEdit', 'tag_user', 'tag_id', 'user_id');
}
}
Country
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
protected $table = 'countries';
public function country_user() {
return $this->belongsToMany('App\UserEdit', 'country_user', 'country_id', 'user_id');
}
//protected $fillable = ['region', 'country'];
}
language
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class language extends Model
{
protected $table = 'languages';
//protected $fillable = ['user_id','language_id'];
public function language() {
return $this->belongsToMany('App\UserEdit', 'language_user', 'language_id', 'user_id');
}
}
Ok. So this was a problem caused by a function of how Laravel works.
After some trial and error, I made a guess that Laravel only checks for id's when using pivot magic methods and does not search for any other data.
So I wrote a function to get the preferences, explode them out into the original tags, then compare that tag against the preference list, get the id associated with that tag and then print it to the pivot table with the other data. For testing and double checking purposes the original tag was initially included, then removed later.
public function move() {
//get the preferences from the users table (this is stored badly "1-7,3-4,4-6,6-6,6-10,8-5,8-9,8-10,8-13,9-3,9-9") this returns a collection
$prefs = DB::table('users')->select('id', 'Preferences')->where('Preferences', '!=', '')->get();
//iterate through each item in the collection
foreach ($prefs as $pref) {
//get the tags only
$tags = $pref->Preferences;
//break up the tags into a searchable array by exploding each preference at the comma
$tag = explode(',', $tags);
//for each tag in the array assign it to t
foreach ($tag as $t) {
//get the id from the preference table when the tag matches t
$new = DB::table('prefs')->select('id')->where('tag', $t)->first();
//make a new model instance to insert data
$taguser = new Tag(array(
'user_id' => $pref->id,
'tag_id' => $t,
'tags' => $new->id
));
//insert data
$taguser->save(); //save in table
//dd($taguser); <- Since we have a lot of items, dd will break the printout after the first, meaning you can check to see if your information is being handled properly
}
}
return view('refactor');
getting rid of the original tag-id and replacing it with just the plain id that referenced the tag meant that laravel could now search against the correct data and return useful results.
I am trying to grasp the concept of Eloquent ORM by creating a ticketing system at the moment. What I am trying to achieve is:
The tickets with the user who posted the ticket
The feedback belonging to the ticket and the user who entered the
feedback
This is what I have right now:
// TicketController.php
public function index()
{
$tickets = Ticket::with('feedback')->with('user')->orderBy("created_at", "desc")->get();
//dd($tickets);
return View::make('modules.helpdesk.index')->withTickets($tickets);
}
And the following models
// Ticket.php
class Ticket extends Eloquent {
protected $table = 'helpdesk_tickets';
public function feedback()
{
return $this->hasMany('Feedback');
}
public function user()
{
return $this->belongsTo('User');
}
}
// Feedback.php
class Feedback extends Eloquent {
protected $table = 'helpdesk_tickets_feedback';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}
// User.php
class User extends Eloquent {
protected $table = 'users';
public function ticket()
{
return $this->belongsTo('Ticket');
}
}
What I have now is the tickets, their related feedback and user who created the ticket. What I am trying to achieve now is to also get the user who created the feedback.
You need to fix the relation:
// User model
public function tickets()
{
return $this->hasMany('Ticket'); // adjust namespace if needed
}
Next add the relation:
// Feedback model
public function user()
{
return $this->belongsTo('User'); // namespace like above
}
then use eager loading:
// it will execute 4 queries:
// 1st for tickets
// 2nd for feedback
// 3rd for feedbacks' user
// 4th for tickets' user
$tickets = Ticket::with('feedback.user', 'user')->latest()->get();
you can then access the relations in a loop, like below:
#foreach ($tickets as $ticket)
{{ $ticket->title }} by {{ $ticket->user->name }}
#foreach ($ticket->feedback as $feedback)
{{ $feedback->content }}
#endforeach
#endforeach
What you want to do is create nested relations, just like Ticket add a belgonsTo relation on feeback
When you want to use it you can chain relations using the dot notation feedback.user
The code
// Feedback.php
class Feedback extends Eloquent {
protected $table = 'helpdesk_tickets_feedback';
public function ticket()
{
return $this->belongsTo('Ticket');
}
public function user()
{
return $this->belgonsTo('User')
}
}
// TicketController.php
public function index()
{
$tickets = Ticket::with('feedback')->with('user')->with('feedback.user')->orderBy("created_at", "desc")->get();
//dd($tickets);
return View::make('modules.helpdesk.index')->withTickets($tickets);
}
EDIT:
Even though this would work, it will execute more queries than needed. See Jareks answer.
Original Answer:
First of all you need to get your relationships straightened, in User.php you should call the user relationship with HasMany.
public function ticket() {
return $this->hasMany('Ticket');
}
In modules.helpdesk.index you should now have a Ticket Collection since your attaching the $ticket variable to the view.
If you loop through this collection with a foreach loop then what you should get is a model each loop:
foreach($tickets as $ticket) {
// Prints the name property of the Ticket model
print $ticket->name;
// Since a ticket only belongs to ONE user then that means that you are trying to fetch a model
// What we're doing here is getting the User model via the relationship you made in the model Ticket.php and then getting the name.
print $ticket->user()->first()->username;
// Since a ticket can have MANY feedbacks that means were fetching a collection
// which needs to be broken down to models so we do that looping the collection.
// Here we are doing the same thing as with the User model except with a collection.
foreach($ticket->feedback()->get() as $feedback) {
$feedback->text;
}
}
You should definitely check out the Laravel API and see Collection and Model there. http://laravel.com/api/ You get alot of help from there when you get stuck, trust me :)
I hope this answered your question.
I'm learning Laravel right now and i have following tables and resources (models, controllers, ect.):
tickets
- id
- title
- projectID
- statusID
projects
- id
- title
status
- id
- title
I have to make a list of my Tickets on the Startpage. Not nessesary to say that i need the Project- and Statustiltles and not the IDs. Currently i do:
Route::get('/', function()
{
$tickets = Ticket::all();
return View::make('layout')->with('tickets', $tickets);
});
My current output is:
tickets->id, tickets->title, tickets->projectID, tickets->statusID
The output i want is
tickets->id, tickets->title, tickets->projects->title, tickets->status->title
So i hope anyone can understand what i'm trying to ask here and maybe provide me some help. Thank you!
Resolution: I had to set the foreign_keys first in my DB. Then i used the relationships mentioned in the answers and it works fine.
My Model:
class Ticket extends \Eloquent {
protected $fillable = [];
public function project()
{
return $this->hasOne('Project', 'id', 'projectID');
}
public function status()
{
return $this->hasOne('Status', 'id', 'statusID');
}
}
My View:
#foreach($tickets as $key => $value)
...
<td>{{ $value->project->title }}</td>
<td>{{ $value->status->title }}</td>
...
#endforeach
If you configure you relationships correctly you can do that without problems using the Laravel Eager Loading feature, for example:
Eager Loading (Laravel docs)
Eager loading exists to alleviate the N + 1 query problem...
class Ticket extends Eloquent {
public function project()
{
return $this->belongsTo('Project', 'projectID', 'id');
}
public function status()
{
return $this->belongsTo('Status', 'statusID', 'id');
}
}
Now, just call the fields you want, for example:
foreach (Ticket::all() as $ticket)
{
echo $ticket->project->title;
echo $ticket->status->title;
}
Obs.: In your return object/array you can't see the relationships fields unless you do manual joins, etc. So, just configure your relationships and call the fields you want.
Sorry for my english
Define relationships specifying custom foreign keys (defaults would be status_id and project_id for your models):
// Ticket model
public function project()
{
return $this->belongsTo('Project', 'projectID');
}
public function status()
{
return $this->belongsTo('Status', 'statusID');
}
Then eager load related models:
$tickets = Ticket::with('project','status')->get();
// accessing:
foreach ($tickets as $ticket)
{
$ticket->status; // Status model with all its properties
$ticket->project; // Project model
}
I have troubleshoot all day with eager loading/n+1 issue, researched and read and watched tutorials about this issue, but haven't solved it yet. I have set up the relationships for the models, but when I passing in the data with a helper function I got this n+1 issue.
I want to grab an artist name from the url site.com/Artist/songs and get all its songs and display an url like this.
site.com/$Artist/songs/$id
My artists/index.blade.php view looks like this http://i61.tinypic.com/2nqzatk.jpg
I'm not sure what I'm missing here.
Thanks in advance!
My tables
songs
id, title, body, slug, hits, artist_id, created_at, updated_at
artists
id, name, body, created_at, updated_at
routes.php
Event::listen('illuminate.query', function($query)
{
var_dump($query);
});
...
Route::get('{artist}/songs', 'ArtistsController#index');
Route::get('{artist}/songs/{id}', ['as' => 'artist.songs.show', 'uses' => 'ArtistsController#show']);
Model: Song.php
class Song extends Eloquent {
protected $guarded = ['id'];
/**
* Setting up relationship for the artist model for easier usage
*
*/
public function artist()
{
return $this->belongsTo('Artist');
}
// Override find method
public static function find($id, $name = null)
{
$song = static::with('artist')->find($id);
// If the song is found
if ($song)
{
// If the song doesn't belong to that artist, throw an exception which will redirect to home, defined in global.php
if ($name and $song->artist->name !== $name)
{
throw new Illuminate\Database\Eloquent\ModelNotFoundException;
}
return $song;
}
// If the song is not found, throw an exception which will redirect to home, defined in global.php
else
{
throw new Illuminate\Database\Eloquent\ModelNotFoundException;
}
}
// Get songs from artist
public static function byArtist($name)
{
return Artist::byName($name)->songs;
}
}
Model Artist.php
class Artist extends Eloquent {
protected $fillable = [];
/**
* Setting up relationship with the song model for easier usage
* $artist->songs;
*/
public function songs()
{
return $this->hasMany('Song');
}
// Get artist by name
public static function byName($name)
{
return static::whereName($name)->first();
}
}
Controller: ArtistsController.php
class ArtistsController extends BaseController {
// Set default layout for this controller
protected $layout = 'layouts.master';
/**
* Display a listing of the resource.
* GET /artists
*
* #return Response
*/
public function index($name)
{
$this->data['songs'] = Song::byArtist($name);
$this->layout->content = View::make('artists.index', $this->data);
}
helpers.php
function link_to_artist_song(Song $song)
{
return link_to_route('artist.songs.show', $song->title, [$song->artist->name, $song->id]);
}
Index view for the artists
artists/index.blade.php http://i61.tinypic.com/2nqzatk.jpg
#extends('layouts.master')
#section('content')
#if(isset($songs))
<h1>All Songs</h1>
<ul class="list-group">
#foreach($songs as $song)
<li class="list-group-item">{{ link_to_artist_song($song) }}</li>
#endforeach
</ul>
#endif
#stop
You never eager load anything, that's why you could be facing n+1 issue.
If I get your right, what is a bit hard with the code you have here, you want all songs of given artist with $name from the url, right?
So here's everything you need to make it work:
// controller
public function index($name)
{
// with('songs') is eager loading related songs for you
$this->data['artist'] = Artist::with('songs')->whereName($name)->first();
$this->layout->content = View::make('artists.index', $this->data);
}
// the problem of your queries is in the helper:
function link_to_artist_song(Song $song)
{
return link_to_route('artist.songs.show', $song->title, [
$song->artist->name, // this is calling db query for each song to retrieve its artist (despite it is always the same)
$song->id]);
}
// so instead use this in your view
#foreach($artist->songs as $song)
<li class="list-group-item">
{{ link_to_route('artist.songs.show', $song->title, [$artist->name, $song->id]) }}
</li>
#endforeach
The n+1 problem exists when you have lots of songs with lots of artists. It has to get all the songs (1 query) and then for each song, get the artists (n queries).
In this case, you already know the song, so that's one query, then you need all the artists for that song, which is just one additional query.
The n+1 problem would only come into play if you were trying to find a song of a certain genre for example, which could return many songs. Then for each song, you would then have to make an additional query to get that song's artists. This would be where eager loading is most useful.
$song = Song::find($id);
$artist = $song->artist;
Every time you grab a song, and do $song->artist it will do a query.
You can also use Query scopes.
class Song extends Eloquent {
public function scopeByArtist($query, $name) {
return Artist::whereName($name)->first()->songs(); //->with('artist');
}
}
Like this the artist is already loaded.
Query with:
$songs = Song::byArtist($name)->get();