BadMethodCallException: Method does not exist - php

I am trying to create a many to many relationship between this profile and an availability table but within my test i keep getting call to a undefined method on availability in the test.
This is the controller function
/**
* Creates association between availability and podcast profile
*
* #param array $availabilities
*/
private function associateAvailability(array $availabilities)
{
$this->podcastProfile->availability()->sync(
array_map(function ($availability) {
$availabilityModel = Availability::where('availability', '=', $availability)->first();
return $availabilityModel->id;
}, $availabilities)
);
}
This is the method in the podcast profile model
/**
* Defines many-to-many relationship between podcasts and availabilities
*/
public function availability(): BelongsToMany
{
return $this->belongsToMany(
'App\Models\Availability',
'podcast_availability',
'podcast_profile_id',
'availability_id'
);
}
This is the test for the method
/**
* #test
*/
public function it_should_create_availability_relationship()
{
$this->handlePostRequestToController();
$this->assertTrue($this->user->podcastProfile->availability()->exists());
$this->checkAvailability($this->requestData['availability']);
}
this is the check availability method inserted into the test
/**
* Check database
*
* #param $availabilities
*/
private function checkAvailability($availabilities): void
{
foreach ($availabilities as $availability) {
$availabilityModel = Availability::where('availability', '=', $availability)
->first();
$this->assertDatabaseHas('podcast_availability', [
'podcast_profile_id' => $this->user->podcastProfile->id,
'availability_id' => $availabilityModel->id
]);
}
}
this is the error
1) Tests\Feature\PodcastProfileControllerTest::it_should_create_availability_relationship
BadMethodCallException: Method Illuminate\Database\Eloquent\Collection::availability does not exist.

If your trying to make a Many to Many relationship base on Laravel Many to Many Relationship.
Here's how you do it. You need to have to 2 models and 3 migrations.
FIRST
Your model should look like this:
Profile Model
protected $guarded = [];
public function availabilities() {
return $this->belongsToMany(Availability::class);
}
Note: I use availabilities because it is in a many to many relationship so its a better naming convention.
Availability Model
protected $guarded = [];
public function profiles() {
return $this->belongsToMany(Profile::class);
}
SECOND
Your migration should be like this:
Profile Migration
Schema::create('profiles', function (Blueprint $table) {
$table->bigIncrements('id');
...
$table->timestamps();
});
Availability Migration
Schema::create('availabilities', function (Blueprint $table) {
$table->bigIncrements('id');
...
$table->timestamps();
});
Availability And Profiles Migration
Schema::create('availability_profile', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('availability_id');
$table->unsignedBigInteger('profile_id');
$table->timestamps();
});
Note: I use the availability_profile naming convention in alphabetical order
INFO
You can generate this migration using artisan command like this php artisan make:migration create_availability_profile_table --create=availability_profile
LAST
In you controller you can assign the profile to availability
Controller
Assuming you have record on your database.
public function generateAvailability() {
$profile = Profile::firstOrFail(1);
$role = Role::firstOrFail(1);
$profile->availabilities()->attach($role->id);
dd(profile->availabilities);
}
Note: I use dd(dump and die) to check the record
You can also see this reference and this

Related

Retrieve data from many to many relationship in Laravel

I know the question has been responded many times, but for some reason I couldn't make it work for my tables, no matter what, and I don't understand why.
I've been trying this for like 4 hours and I couldn't get it done right.
So here are my functions from two models:
ConducatorDoctorat
public function materii()
{
return $this->belongsToMany('App\DomeniuDoctorat', 'profesor_domeniu', 'domeniu_id', 'profesor_id');
}
DomeniuDoctorat
public function materii()
{
return $this->belongsToMany('App\ConducatorDoctorat', 'profesor_domeniu', 'profesor_id', 'domeniu_id');
}
and profesor_domeniu schema:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ProfesorDomeniu extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('profesor_domeniu', function (Blueprint $table) {
$table->unsignedBigInteger('profesor_id');
$table->unsignedBigInteger('domeniu_id');
$table->foreign('profesor_id')
->references('id')->on('conducatori_doctorat')
->onDelete('cascade');
$table->foreign('domeniu_id')
->references('id')->on('domenii_doctorat')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('profesor_domeniu');
}
}
In my controller, I tried so many ways to do it, for example, like this:
public function edit($id)
{
$materii = ConducatorDoctorat::findOrFail($id)->materii()->pluck('domeniu_id');
return view('admin.conducatori_doctorat.edit')->with([
'materii' => $materii
]);
}
but it still doesn't work.
With the given $id, I want to retrieve all the data from profesor_domeniu where profesor_id == $id.
That's all, but I can't get it.
How can this be done and why doesn't my approach work?
//edit for clarity:
conducator_doctorat is where the professors are stored and domenii_doctorat is where their fields are stored.
In profesor_domeniu, I store what each professor teaches, by linking an id from conducatori_doctorat to an id of a field from domenii_doctorat.
//edit2:
materii() means the fields they teach.
//edit3:
My many-to-many relationship with some data added into the pivot table profesor_domeniu.
eager load your relationship on your model :
public function edit($id)
{
$conducatorDoctorat = ConducatorDoctorat::with('materii')->findOrFail($id);
$materii = $conducatorDoctorat->materii;
return view('admin.conducatori_doctorat.edit')->with([
'materii' => $materii
]);
}
you can also request only your materii related to your conducatorDoctorat :
public function edit($id)
{
$materii = DomeniuDoctorat::whereHas('materii', function($query) use ($id){
$query->where('id', $id);
})->get();
return view('admin.conducatori_doctorat.edit')->with([
'materii' => $materii
]);
}

morphToMany real many to many relation

All polymorh examples I found are one to many if I get it correct. (Tag to Post / Video e.g)
In my Case the parent class is multiple and the child class also. Therefore i set up the pivot table
Tables
Person
id
Venture
id
Capital
id
Estate
id
PIVOT TABLE
Revenues
emitter_id //ID of the revenue emitting class (Venture, Capital, Estate)
emitter_type // Class of the Emitter (App\Models\Venture App\Models\Estate)
receiver_id // Id of Receiver (Venture or Person)
receiver_type // type of Receiver (App\Models\Venture or App\Models\Person)
revenue
In the Estate Model i try this
public function revenuePersons()
{
// searched type, own type/id ,tabel own ID to search id
return $this->morphToMany(Person::class, 'emitter' ,'revenues' ,'emitter_id','receiver_id')
->withPivot('revenue');
}
One the Person Model
public function estaterevenues(){
// searched type, own type/id ,tabel own ID to search id
return $this->morphToMany(Estate::class, 'receiver' ,'revenues' ,'receiver_id','emitter_id')
->withPivot('revenue');
}
The Code works but in some cases i get additional relations back. So it seams the searched _type is not correctly considered.
So i started to implement a own database query function that gives me the Revenue Entry back. It works correctly.
Revenue Model
public function getRevenue($ownside, $emitter_id = Null, $emitter_type,$receiver_id=Null, $receiver_type ){
$revenue = DB::table('revenues')
->where('emitter_id', $emitter_id)
.....()->get()}
But I am not able to do something like
$persons->getRevenues
because a Relationship is expected as return value
So if anyone has an idea how to do that correctly I would be very happy. Or some other best practices for this many to many approach.
The second Question is how to get all revenue receiver at once.
Instead of
$estate->revenuepersons
$estate->revenueventures
Have something like
$estate->revenues //that list both, Ventures and Persons
And here a Class Diagram
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* Get all of the tags for the post.
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
/**
* Get all of the tags for the post.
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
/**
* Get all of the posts that are assigned this tag.
*/
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
/**
* Get all of the videos that are assigned this tag.
*/
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
$post = Post::find(1);
dd($post->tags);
$video = Video::find(1);
dd($video->tags);
$tag = Tag::find(1);
dd($tag->posts);
$tag = Tag::find(1);
dd($tag->videos);
posts table migration:
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string("name");
$table->timestamps();
});
videos table migration:
Schema::create('videos', function (Blueprint $table) {
$table->increments('id');
$table->string("name");
$table->timestamps();
});
tags table migration:
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->string("name");
$table->timestamps();
});
taggables table migration:
Schema::create('taggables', function (Blueprint $table) {
$table->integer("tag_id");
$table->integer("taggable_id");
$table->string("taggable_type");
});

Im unable to delete the records present int he Pivot Table

I have five tables:
Post
category
Tags
category_post
post_tag
The problem that I am getting is that if I delete the post then it should also delete all the relations of that post in all the tables where it is related. But the system is performing the total opposite it is only deleting the post in the post table.
I found a solution which was
$table->engine='InnoDB'
but my problem still remains the same
This is my Migration for the Category_post Pivot Table
public function up()
{
Schema::create('post_tag', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->integer('post_id')->index()->unsigned();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->integer('tag_id')->index()->unsigned();
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->timestamps();
});
}
This is what I am doing in the controller
public function destroy(Post $post)
{
$post=Post::find($post->id);
$post->delete();
return redirect('admin/post')->with('message','Deleted Sucessfully');
}
I also Tried this
public function destroy(Post $post)
{
$post=Post::find($post->id);
$post->categories()->delete();
$post->tags()->delete();
$post->delete();
return redirect('admin/post')->with('message','Deleted Sucessfully');
}
But got the same results
When using pivot tables for ManyToMany relationships in Laravel, you should detach the associated tags and categories with the Post model instead of deleting them as per the docs
Besides, your controller code is deleting the tags and categories models and not the association which would corrupt any other posts that are attached to those tags and categories.
Here's an example of the correct way to do it
In your tags migrations
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->bigIncrements('id');
// Any other columns goes here
$table->timestamps();
});
Schema::create('post_tag', function (Blueprint $table) {
$table->bigInteger('post_id');
$table->bigInteger('tag_id');
// ensures a specific post can be associated a specific tag only once
$table->primary(['post_id', 'tag_id']);
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('post_tag');
Schema::dropIfExists('tags');
}
Do the same thing for categories migration
Specify the ManyToMany relationship in your Eloquent model like so
class Post extends Model
{
public function tags()
{
return $this->belongsToMany('App\Tag');
}
public function categories()
{
return $this->belongsToMany('App\Category');
}
}
Now when associating tags/categories with a post use the attach method
$post = Post::create([]); // this is only sample code, fill your data as usual
$tag = Tag::create([]);
$category = Category::create([]);
// You can either attach by the model itself or ID
$post->tags()->attach($tag);
$post->categories()->attach($category);
And finally when destroying the Post model, just deassociate the relationship with the tags and categories instead of deleting them using the detach method like so
public function destroy(Post $post)
{
$post->categories()->detach();
$post->tags()->detach();
$post->delete();
return redirect('admin/post')->with('message','Deleted Sucessfully');
}

Specific role relations

I work on one project with Laravel 5.2 and Entrust package for ACL.
In this project I need for one Role ('venue_owner') in which venue is owner. I have also table called venue and I have no idea how to make this relations, because table users is general for all type of users.
How to make this relations to know what user from role venue_owner is owner of what venues ?
Have you created your Migrations yet by running: php artisan enthrust:migration? if not it, run it and then inside the file that is generated, add your own tables like below within the up() Method of the Enthrust Migration File:
<?php
public function up() {
// SOME OTHER TABLE CREATION CODES...
Schema::create('venue_owner', function (Blueprint $table) {
$table->increments('id');
$table->integer("user_id")->unsigned();
$table->timestamps();
// CREATE THE ASSOCIATION/RELATIONSHIP USING FOREIGN KEY
$table->foreign('id')
->references('id')
->on('venue')
->onDelete('cascade');
});
Schema::create('venues', function (Blueprint $table) {
$table->increments('id');
$table->integer("venue_owner_id")->unsigned();
$table->string("venue");
$table->timestamps();
// CREATE THE ASSOCIATION/RELATIONSHIP USING FOREIGN KEY
$table->foreign('venue_owner_id')
->references('id')
->on('venue_owner');
});
}
public function down() {
// OTHER DROP COMMAND CODES...
Schema::drop('venue_owner');
Schema::drop('venues');
}
Then in your Eloquent Model Class you can explicitly set the $this->hasMany() like so:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VenueOwner extends Model {
/**
* GET ALL THE venues FOR THE venue_owner .
*/
public function venues() {
return $this->hasMany('App\Venues');
}
/**
* GET ALL THE user FOR THE venue_owner .
*/
public function user() {
return $this->hasOne('App\User');
}
And in your Venues Eloquent Model Class, you do something like:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Venues extends Model {
/**
* GET THE venue_owner FOR venue(s).
*/
public function venueOwner() {
return $this->belongsTo('App\VenueOwner');
}
Last but not least in your Users Eloquent Model Class, you do something like:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Users extends Model {
/**
* GET THE user Information FOR venue_owner.
*/
public function venueOwner() {
return $this-> hasOne('App\VenueOwner');
}
Now, you can get all information about the venue_owner and his venues and roles & permissions using the user_id.

Laravel Eloquent one-to-one relationship setup

I am setting up a one to one relationship in a laravel application, but I am getting the error "trying to get property of non-object" when I try to reference a related table.
My Contractor.php model:
class Contractor extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'contractor';
function profile() {
return $this->hasOne('ContractorProfile');
}
}
My ContractorProfile.php model:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class ContractorProfile extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'contractor_profile';
public function contractor() {
return $this->belongsTo('Contractor');
}
}
here is the snippet of my view file
show.blade.php
<div>
<h4>{{ $contractor->profile->tag_line }}</h4></p>
</div>
If I just call $contractor->profile the page loads but nothing is echoed back. If I add the ->tag_line, I get the "trying to get property of non-object" error. tag_line is a column name inside of my contractor_profile table.
Do you see an error that I am making?
TIA
EDIT: Database info:
Schema::create('contractor', function ($table) {
$table->increments('id');
$table->integer('hba_number')->nullable();
$table->integer('msn')->nullable();
$table->string('type')->default("");
$table->string('name')->default("");
$table->string('address_1')->default("");
$table->string('address_2')->default("");
$table->string('city')->default("");
$table->string('state')->default("");
$table->string('zip')->default("");
$table->string('country')->default("");
$table->string('phone')->default("");
$table->string('website')->default("");
$table->integer('company_id')->nullable();
$table->integer('user_id');
$table->timestamps();
$table->softDeletes();
});
Schema::create('contractor_profile', function ($table) {
$table->increments('id');
$table->integer('contractor_id');
$table->string('tag_line')->default("");
$table->string('story')->default("");
$table->string('area_of_operation')->default("");
$table->text('experience')->default("");
$table->text('education')->default("");
$table->text('insurance_verified')->default("");
$table->timestamps();
$table->softDeletes();
});
The issue may be because you're using a non-standard table name for your Contractor model. Try defining the relationship in your ContractorProfile model by specifying the second parameter to belongsTo:
return $this->belongsTo('Contractor', 'contractor_id');
You may also need to perform the same mapping on the Contractor model as well:
return $this->hasOne('ContractorProfile', 'contractor_id');

Categories