I'm trying to fetch covers of the books that belongs to an author. So far, so good. But it generates a separate query for each book and takes 2 seconds to load a page, I think I'm doing something wrong.
I use eager loading with my comments table (a comment belongs to a user), but since I use polymorphic relations with images table (a image can belong to different kind of other tables, such as user, thing, or group, so I can't use foreign keys in images table since it's not a right convention), I couldn't find a way to achieve the same thing this time.
Image Model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
public function imageable()
{
return $this->morphTo();
}
}
Person Model (Author)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Person extends Model {
public function books()
{
return $this->belongsToMany('\App\Models\Thing', 'person_thing');
}
Thing Model (Books)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Thing extends Model {
public function cover() {
return $this->morphMany('\App\Models\Image', 'imageable');
}
}
Controller
$findBooks = Person::with(array('books' => function($query)
{
$query->groupBy('original_name');
}))->find(52957);
$allbooks = $findBooks->books;
return view('frontend.index')->with('allbooks', $allbooks)
}
Current View
#foreach($allbooks as $allBooks)
#foreach($allBooks->cover as $value)
<img class="hund" src="{{$value->link}}" alt="">
#endforeach
#endforeach
Image:
From this post on Laracasts
$query->with([
'child' => function ($q) {
$q->where(’someCol', ’someVal’); //constraint on child
},'child.grandchild' => function ($q) {
$q->where(‘someOtherCol’, ‘someOtherVal’); //constraint on grandchild
}
]);
I don't think your problem is with eager loading or morph to many if your page takes 2s to load, ,Did you install laravel/debugbar to see exactly what takes 2 sec or how many queries u run?
Related
It's a common situation but I somehow couldn't manage to find a reasonable solution so far. Basically I need the following:
I have the model House which contains a couple of other models (e.g. Brick, 'Stick', 'Chair' etc.). Each one of them has a belongsTo relation to the House (so basically in the sticks, chairs and bricks table there is a house_id column). Each model contains a public function house() method which returns the house they're part of. Now comes the problem that I'd like to get all of a house's supplies by a method, let's say for example public function supplies(), which should return a collection of instances of the classes Stick, Chair and Brick. Is there any convenient way to achieve that? Could I for example unite those classes by a trait and somehow point to that trait in the house's method or maybe with a BaseClass, let's say Supply, that all the supplies should inherit? Any recommendances? Any help is greatly appreciated and thanks in advance!
There is no built-in support for this in Laravel.
You can define separate HasMany relationships and then merge them with an accessor:
class House extends Model {
public function bricks() {
return $this->hasMany(Brick::class);
}
public function chairs() {
return $this->hasMany(Chair::class);
}
public function sticks() {
return $this->hasMany(Stick::class);
}
public function getSuppliesAttribute() {
return $this->bricks->toBase()->merge($this->chairs)->merge($this->sticks);
}
}
$supplies = $house->supplies;
+++ UPDATE +++
I've created a package for merging relationships using views:
https://github.com/staudenmeir/laravel-merged-relations
First, create the merge view in a migration:
use Staudenmeir\LaravelMergedRelations\Facades\Schema;
Schema::createMergeView(
'supplies', [(new House)->bricks(), (new House)->chairs(), (new House)->sticks()]
);
Then define the relationship:
class House extends Model
{
use \Staudenmeir\LaravelMergedRelations\Eloquent\HasMergedRelationships;
public function supplies()
{
return $this->mergedRelation('supplies');
}
}
Use it like any other relationship:
House::find($id)->supplies;
House::find($id)->supplies()->paginate();
House::with('supplies')->get();
I want to have my model automatically call its relations when instantiated. As of now my model looks like this:
class AdminLog extends Model{
public function __construct(){
$this->belongsTo('App\User', 'admin_id');
}
}
but when i try to do dd(AdminLog::get()->first());, it doesnt show any relations.
Edit#1: tried adding parent::__construct(); inside the model's __construct method but it didn't work.
belongsTo() defines a relationship, it doesn't load it.
First you need to define the relationship, then you can load it at any point using the load method.
class AdminLog extends Model {
public function user() {
return $this->belongsTo(\App\User::class, 'admin_id');
}
}
$log = AdminLog::first();
$log->load('user');
It is possible to load inside the constructor, but I would highly recommend against that. If you have 20 AdminLog objects then it will query the database 20 times, once for each object. That's inefficient.
What you should do instead is use eager loading. This will query the users table just once for all 20 admin logs. There are many ways to do this, here is an example:
$logs = AdminLog::take(20)
->with('user')
->get();
dd($logs->toArray());
i have a question on mongodb, model cakephp and relationships.
I'd create the following relations:
User -> hasMany -> City
City -> belongsTo -> User
In MongoDB, I have two tables:
users
cities (with key user_id)
In cakephp, I have 2 model:
User.php
class User extends Model {
public $name = 'User';
public $actsAs = array('Containable');
public $hasMany = array ('City');
..
}
and:
City.php
class City extends Model {
public $name = 'City';
public $actsAs = array('Containable');
public $belongsTo = array('User');
..
}
In my controller I use :
$user = $this->User->find('all');
but it doesn't work. In sql dump, cakephp uses a find only on tbl users.
Why? Where I wrong?
I normally place recursive to -1 and containable in app model, so it applies to all models you create unless you override specifically.
class AppModel extends Model {
public $actsAs = array('Containable');
public $recursive = -1;
}
Your relationships are fine, although I usually add className and foreignKey just to be safe and clear. In your controller you should do something like this:
$users = $this->User->find('all', array(
'contain' => array(
'City'
)
));
Recursive will prevent any associated records being included by default, this is good as sometimes you do not need the recursive data and extra data will help slow down your application.
Next adding contain into your find call may seem like a chore but it will be clear and concise what you are querying, any 3rd party developer will understand exactly what you are doing if they know how to use Cake. Hope this helps.
I want to populate a dropdown from a table 'accountgroups' which has 2 columns id and name. The first row of dropdown should be blank or '--Select Account Group--' when first loaded. Once the user selects the item (i.e. display field 'name' and value field 'id') I want to get id and name values when form is submited.
You'll need to create a model for your table first:
class Model_AccountGroups extends Model_Table {
public $table='accountgroups';
function init(){
parent::init();
$this->addField('name');
}
}
$form->addField('dropdown','account_id')->setModel('AccountGroups');
if($form->isSubmitted()){
$form->js()->univ()->alert('Selected ID='.$form->get('account_id'))->execute();
}
Probably (your post have an year and a half) you had resolved your issue, but I want anyway contribute with "my 2 cents" for this issue. I looked around (documentation, forums, etc) and neither found another solution to the same problem. My solution (tested and working) is this (please is is not efficient enough let me know):
>>>>>>>>>>>>>>>>>>>>> ON MODEL
class Model_Offices extends Model_Table {
public $entity_code='Offices';
function init(){
parent::init();
$this->addField('ID')->ReadOnly(True);
$this->addField('OfficeName');
}
function GetAll() {
$r=array();
$AllOffices = $this->SetModel('Offices');
foreach($AllOffices as $OneOffice) {
$r[$OneOffice['ID']]=$OneOffice['OfficeName'];
}
return $r;
}
}
>>>>>>>>>>>>>>>>>>>> ON PAGE
$m=$this->Add('Model_Offices');
$form->addField('dropdown','Office')->SetValueList($m->GetAll()) ;
Mack
I am a little confused in this Doctrine model concept , lets say we a table called "article"
Doctrine will generate class called
i am using Zend framework and Doctrine 1.2
models/generated/BaseArticle.php
models/ArticleTable.php
models/Article.php
Is it true to call the ArticleTable in the controller in this way
$tableArticle = Doctrine::getTable('Article');
then to save it in the Article Object like this
$article = new Article();
$fArticles = $tableArticle->getFeaturedArticles();
foreach ($fArticles as $fArticle) {
$article->fromArray($fArticle);
echo $article->title
}
Or I have to let the Article.php to call the ArticleTable ?
then to initiate an Article.php object in the controller ?
class Article extends BaseArticle
{
public function getFArticles()
{
$tableArticle = Doctrine::getTable('Article');
$obj = $tableArticle->getFeaturedArticles();
return $obj;
}
Article.php should not call ArticleTable.php unless really, really needed. In table classes you will only hold queries called by controller like:
$featuredArticles = ArticleTable::getInstance()->getFeatured() ;
Above code is simpler and you will have autocompletion in any IDE.
The reason not to call queries in Article.php is that you will have easier transition to Doctrine2 one day.
For a table call tbl_article or just article, doctrine will generate Article.php and BaseArticle.php. Base classes must not be changed manually.
Article class is where your logic goes. For example, you fetch list of ALL articles in database. When you display them, you want feature articles to have a star (just an example):
controller:
$allArticles = ArticleTable::getInstance()->findAll() ;
template (Smarty version here):
{foreach $allArticles as $article}
{if $article->isFeatured()} <img src=.. some image ..>{/if}
<h5>{$article->title}
{/foreach}
and the model class
class Article extends BaseArticle
{
const STATUS_FEATURED = 1 ;
public function isFeatured()
{
return $this->status == self::STATUS_FEATURED ;
}
}
All these are just some examples, in real life it is much more usefull.
And what are you actually trying to do with this fromArray($fArticle)? I don't see any point of that code.