I defined an Eloquent model in Laravel AssessmentCategory with a with relation with another Eloquent model AssessmentQuestion:
<?php
namespace App\Http\Models;
use Illuminate\Database\Eloquent\Model;
class AssessmentCategory extends Model {
protected $table = 'assessment_categories';
protected $with = ['questions'];
public function scopeJsonColumns($query) {
return $query->selectRaw('assessment_categories.id, assessment_categories.title');
}
public function questions() {
return $this->hasMany(AssessmentQuestion::class, 'category_id');
}
}
the AssessmentCategory now always loads its relation (child) data, but I want to call the AssessmentCategory somewhere without loading the relation (child) data.
AssessmentCategory::jsonColumns()->get();
You can use without() Method when want to get data without relation.
AssessmentCategory::without('questions')->jsonColumns()->get();
There is multiple way to fix you problem.
You can disable eager loading for current query at all:
AssessmentCategory::setEagerLoads([])->get();
You can exclude only one relation from eager loading:
AssessmentCategory::without('questions')->get();
Or you can disable eager loading at all by removing:
protected $with = ['questions'];
note that in this case you need yo overwrite all previous queries.
Related
I have a navigation model that can have many items associated with it:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use JetBrains\PhpStorm\ArrayShape;
use Laravel\Scout\Searchable;
class Navigation extends Model
{
use HasFactory;
use Searchable;
protected $guarded = [];
public function navigation_items(): HasMany
{
return $this->hasMany(NavigationItem::class);
}
}
The navigation item model looks like this
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class NavigationItem extends Model
{
use HasFactory;
protected $guarded = [];
public function navigation(): BelongsTo
{
return $this->belongsTo(Navigation::class);
}
public function navigatable(): MorphTo
{
return $this->morphTo();
}
}
Now an item can either be of type Page or Blog, in this case the Page model looks like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use JetBrains\PhpStorm\ArrayShape;
use Laravel\Scout\Searchable;
class Page extends Model
{
protected $guarded = [];
public function navigatable(): MorphOne
{
return $this->morphOne(NavigationItem::class, 'navigatable');
}
}
When I try to save a navigation model and associate it with a item, the following error appears:
SQLSTATE[HY000]: General error: 1364 Field 'navigatable_type' doesn't have a default value
I save the model like this:
foreach ($this->selected as $id) {
$this->navigation->navigation_items()->create([
'navigation_id' => $this->navigation->id,
]);
Where $this->selected is the navigation id, it should automatically get the correct navigatable_type and navigatable_id, but this doesn't seem to be working.
passing in the type and id manually works, but this kinda defeats the point of a polymorphic relationship.
any ideas?
On NavigationItem model, since you defined polymorphic relation as 'navigatable' it is expected that NavigationItem model's table contains navigatable_type and navigatable_id. First please ensure this checks out.
Creating records through relation's base function is not a valid method. It is not clear what you are trying to achieve there but when you want to set relation there is two standard way of achieving it:
1- Associate
When a relation is defined as belongsTo, you may use associate() function. Like so:
$account = Account::find(10);
$user->account()->associate($account);
2- Attach
Attach is used when relation is defined belongsToMany (pivot). It allows you to attach multiple records to a model instance/record.
$user = User::find(1);
$user->roles()->attach($roleId);
So if you want to set a 'navigatable' to a Navigation instance, you may:
$somePageInstance=Page::find(55);
$nagivation->navigatable()->associate($somePageInstance)
$nagivation->save();//remember to save, otherwise it won't be
I created an Eloquent Model :
class VehicleDetails extends Model
{
//
protected $table = 'v_vehicle_details';
protected $primaryKey = 'model_id';
public function tariffs()
{
return $this->hasMany('App\Tariffs', 'vehicle_model_id', 'model_id');
}
}
The table structure for the same is v_vehicle_details is
v_vehicle_details
The table structure for tariffs is
t_tariffs
The Model is being called in controller like :
public function booking_view(){
$vehicle_details = new VehicleDetails();
return $vehicle_details->find(5)->tariffs();
}
What I need is to get vehicle details with all tariffs, But when I try that it throws an error Illuminate\Database\Eloquent\Relations\HasMany could not be converted to string. Can somebody please help, I am new to laravel.
These are not actual tables, But views.
change to
return VehicleDetails::with('tariffs')->find(5);
This is because calling the method ->tarrifs() will return a relationship object in this case HasMany this means you still have to perform the query on this object to get the results.
If you use it as a property ->tarifs without () it will perform the query.
This is the same as tarifs()->get() just a shortcut.
Change your function to:
public function bookingView(){
$vehicle = VehicleDetails::with('tariffs')->find(5);
return view('your.view', compact('vehicle'));
}
The with() will eager load the tariffs relation.
You can acess your tariffs in your view like this:
{{$vehicle->tariffs->anyAttributeYouWantToAccess}}
I have three relational table attached below.
https://drive.google.com/file/d/1q1kdURIwFXxHb2MgdRyBkE1e3DMug7r-/view?usp=sharing
I have also three separate models where defined relation among all of my table's.I can read the City Model's information from Country model using hasManyThrough() relation But cannot read the Country information from City model. I have tried to retrieve City model's using ``hasManyThrough``` but didn't get result (attached as commented country method ). Please read my model and it's relational method here..
Is there someone to help me for getting City model's information using Eloquent method hasManyThrough / hasManyThrough or using inverse of hasManyThrough / hasManyThrough ?
01.
<?php
namespace App\Hrm;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Country extends Model
{
//use SoftDeletes;
protected $fillable = ['name','description','status'];
public function districts(){
return $this->hasMany(District::class);
}
public function cities(){
return $this->hasManyThrough(City::class,District::class);
}
}
02.
<?php
namespace App\Hrm;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class District extends Model
{
//use SoftDeletes;
protected $fillable = ['country_id','name','description','status'];
public function country(){
return $this->belongsTo(Country::class);
}
public function cities(){
return $this->hasMany(City::class);
}
}
3.
namespace App\Hrm;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class City extends Model
{
//use SoftDeletes;
protected $fillable = ['district_id','name','description','status'];
public function district(){
return $this->belongsTo(District::class);
}
// public function country(){
// return $this->hasOneThrough(Country::class, District::class);
// }
Doesn't look like there is a native way to define the inverse of a "hasManyThrough" relationship yet in Laravel. There have been a few issues opened on github to request it, but they were closed.
You could use the staudenmeir/belongs-to-through package if you don't mind installing a third-party package for this functionality. Then you should be able to define a belongsToThrough relationship like this:
class City extends Model
{
use \Znck\Eloquent\Traits\BelongsToThrough;
public function country() {
return $this->belongsToThrough(Country::class, District::class);
}
}
Why can't use parent method?
$city = City::find(1);
$country = $city->district->country();
i just had a similar situation i was able to accomplish a belongsToThrough with hasOneThrough
public function country()
{
return $this->hasOneThrough(
Country::class, // model we are trying to get
District::class, // model we have an _id to
'id', // WHERE `district`.`id` = `city`.`district_id`
'id', // `countries`.`id`
'district_id', // local column relation to our through class
'country_id' // `district`.`country_id`
);
}
what this should generate is
SELECT * FROM `countries`
INNER JOIN `districts`
ON `districts`.`country_id` = `countries`.`id`
WHERE `districts`.`id` = ?
-- ? == city.district_id
Database structure:
City:
id: increments
district_id: integer
...
Country:
id: increments
...
District:
id: increments
country_id: integer
...
we can then do $city->country
note: i have not fully tested this but with the testing that i have done it 'works'
Edit: i originally thought that i needed to leave the localKey
parameter null otherwise the relation wont work. it turns out i didnt
fully understand what that column was doing and that was wrong. That
key is the local column that relates to our through column (unless i
still have more to learn/figure out), when left the value as null, it
would use the local id column which a. is the wrong value, b. can also
be out of range (which is how i discovered it was using the wrong
value)
in my testing i only had two rows, both with the same relations. what
i didnt realize though was that on the "through table" both row 1 and
2 and the same related (relation where are trying to reach) so i didnt
notice the issue right away. hopefully now its all working
I had an accessor set on my Eloquent model that worked fine, but the associated database query was getting run once for every instance of the model I created. On my index page this meant 5 dozen queries.
<?php
class Thingy extends Model {
protected $appends = ["parentType"];
public function getParentTypeAttribute($value) {
return self::where("type"=>$this->type, "parent"=>1)->value("name");
}
}
class ThingyController extends Controller {
public function index() {
$thingys = Thingy::all();
return view("things.index", compact("thingys"));
}
}
To explain briefly: there are two classes of "thingy" in the same database table, the class being indicated by a boolean value named "parent." I want to get the name of the parent when I access the child. I know this should be two tables but it's not.
I wanted to reduce the number of database reads, so I tried changing it to a relationship instead. I figured this way I could take advantage of eager loading.
<?php
class Thingy extends Model {
public function parent() {
return $this->hasOne("Thingy", "id")->where("type"=>$this->type, "parent"=>1);
}
}
class ThingyController extends Controller {
public function index() {
$thingys = Thingy::with(["parent"]);
return view("things.index", compact("thingys"));
}
}
The problem is that within the relationship method, $this is an empty instance of the model, unlike in the accessor, so $this->type is null.
Is there a way to access properties of the model I'm working with from within a relationship method?
Figured that out. Since I'm essentially doing a self-join on the same table, I can specify the "local" and "foreign" ID columns as the column I'm trying to match:
public function parent() {
return $this->hasOne("Thingy", "type", "type")->where("parent"=>1);
}
I guess the key concept was to remember that I'm defining a relationship between two instances of the model, which is independent of the particular instances I'm dealing with.
The application has the models:
Atividade.php
class Atividade extends Eloquent {
public function intervencoes() {
return $this->belongsToMany('Intervencao');
}
}
Intervencao.php
class Intervencao extends Eloquent {
public function atividades() {
return $this->hasMany('Atividade');
}
}
The following code works:
Atividade::find($id)->intervencoes()->attach($intervencao_id);
But, this...
Intervencao::find($id)->atividades()->attach($atividade_id);
Returns an BadMethodCallException:
Call to undefined method Illuminate\Database\Query\Builder::attach()
SOLUTION (thanks to #gnack):
I was trying to set a many-to-many relationship, so just needed to change this...
return $this->hasMany('Atividade');
To this:
return $this->belongsToMany('Atividade');
See the Laravel documentation here:
http://laravel.com/docs/eloquent#inserting-related-models
Basically you have set up two different types of relationships for the same two tables - you've set up a many-to-many and a one-to-many. It looks as though you probably wanted a many-to-many, so you'll need to change this line:
return $this->hasMany('Atividade');
To this:
return $this->belongsToMany('Atividade');
This will set the relationship up as a many-to-many relationship, which will then support the attach() method.
The attach() method is only for many-to-many, for other relationships there's save() or saveMany() and associate() (see the docs linked above).
See the documentation Laravel 5.7
A Comment belongTo an unique Post
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
A Post can Have multiple Comments
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
When you want to update/delete a belongsTo relationship, you may use the associate/dissociate method.
$post= App\Post::find(10);
$comment= App\Comment::find(3);
$comment->post()->associate($post); //update the model
$comment->save(); //you have to call save() method
//delete operation
$comment->post()->dissociate();
$comment->save(); //save() method
attach() is for many-to-many relationships. It seems your relationship is supposed to be a many-to-many but you have not set it up correctly for that.
class Intervencao extends Eloquent {
public function atividades() {
return $this->belongsToMany('Atividade');
}
}
Then the attach() should work
In my case I've two roles() methods that's why it's throwing this error.