I have two tables show_types and venues. With a many-to-many relationship i did create a new pivot as follows
public function up()
{
Schema::create('show_types_venues', function (Blueprint $table) {
$table->integer('show_types_id')->unsigned();
$table->foreign('show_types_id')->references('id')->on('show_types');
$table->integer('venue_id')->unsigned();
$table->foreign('venue_id')->references('id')->on('venues');
});
}
And added to the models
ShowType.php
public function venues()
{
return $this->belongsToMany(Venue::class, 'show_types_venues', 'show_types_id', 'venue_id');
}
Venue.php
public function shows()
{
return $this->belongsToMany(ShowType::class, 'show_types_venues', 'venue_id', 'show_types_id');
}
I'm trying to make this relation show up on Nova using
ShowType.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
BelongsToMany::make('Venues', 'venues', Venue::class)
];
But nothing shows up in my interface. How can i make it show a select multiple where I could add/edit the venues associated with this showType?
Do i need to create something more "manual" ? Thanks
Without luck with the documentation of Nova, I did a workaround using Bejacho's Belongs to Many Fields in which we can easily add the many to many relationships appearing as a multiple selector on Nova.
Having the same setup as i mentioned above the only thing i did was to follow their example:
use Benjacho\BelongsToManyField\BelongsToManyField;
public function fields(Request $request){
return [
..., //If you are using with BelongsToMany native Field, put this field after
BelongsToManyField::make('Role Label', 'roles', 'App\Nova\Role'),
];
}
And worked perfectly for what I needed!
Related
I have two tables, users and profiles. A user has one profile. Also a user has referrals. The referrals are referenced by the column referrer_id in the users' table. So a user has a referrer, and a user can have many referrals.
Define a one-to-one relationship on the User's Model:
public function profile()
{
return $this->hasOne(Profile::class);
}
Define an inverse one-to-one relationship on the User's Model:
public function referrer()
{
return $this->belongsTo(User::class);
}
Define a one-to-many relationship on the User's Model:
public function referrals()
{
return $this->hasMany(User::class, 'referrer_id');
}
Define an inverse one-to-one or many relationship on the Profile's Model:
public function user()
{
return $this->belongsTo(User::class);
}
I wish to retrieve the user's profile, the user's referrals along with their profiles, and the referrals' referrals along with a count of each of the referrals' referrals.
The following Eloquent query works, but doesn't paginate:
namespace App\Http\Controllers;
class ReferralsController extends Controller
{
public function index(Request $request)
{
return $request->user()->loadMissing(['profile', 'referrals' => function ($query) {
$query->with(['profile', 'referrals'])->withCount('referrals');
}]);
}
}
I've tried to add ->paginate() to the query (on both as show below and also one or the other) but it doesn't work:
return $request->user()->loadMissing(['profile', 'referrals' => function ($query) {
$query->with(['profile', 'referrals'])->withCount('referrals')->paginate(2);
}])->paginate(2);
Adding it to the inner function doesn't do anything, and adding it to the main query just retrieves the entire users table.
EDIT
I've realized that adding ->paginate() to the inner function actually does limit the number of rows in the collection, but there is no Paginator instance anywhere, so I don't have access to any of the links to move pages.
Got it to work by doing it separately:
$profile = $request->user()->profile()
->firstOrFail();
$referralsPaginator = $request->user()->referrals()
->with('profile')
->withCount('referrals')
->paginate(2);
I'm creating an taggable table like so:
Schema::create('taggable', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tag_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->unsignedBigInteger('company_id');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->unsignedBigInteger('created_by')->nullable();
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->timestamps();
});
As you can see, next to connecting tags to a Post, Video etc (as per the Laravel docs example), I'd also like to ensure that the row that's added is connected to a Company and User model so I can keep track who it belongs to and who created it, but even more so access properties from those models in controllers and views.
I know that in my Post model I can do:
public function tags()
{
return $this->morphedByMany(\App\Models\Tag::class, 'taggable')->withPivot('created_by', 'company_id', 'created_at');
}
The problem is that this will retrieve just the value for created_by and company_id and not the Eloquent model. Is this possible?
So what I'd like to do is access properties of those relationships in controllers and views like so:
$post = Post::findOrFail(1);
foreach($post->tags as $tag) {
$tag->created_by->name // accessing 'name' property on the `User` model
}
foreach($post->tags as $tag) {
$tag->company->address // accessing `address` property on the `Company` model
}
You must do like below:
first you must define relationship between tags and users
class Tags extends Model
{
public function taggable(): MorphTo
{
return $this->morphTo();
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}
then for achieve that you want you must:
$post = Post::first();
$users = $post->tags()->with('createdBy')->get();
I am following the many to many relationships Laravel tutorial here on Laracasts - https://laracasts.com/series/laravel-5-fundamentals/episodes/21
My challenge to myself is, I have created the pivot table article_tag) to keep track of the many to many relations. Articles can have many tags, and tags can have many articles. So I can runsyncetc to associatetagXtagYtagZtoarticle1`. However I also want to be able to optionally set one of the associated tags as "isGreenTag". Can I do this within the pivot table that is keeping track of the many-to-many relations? Could I add a column "is_green_tag"?
Here is my Article class relationship:
class Article extends Model {
public function tags() {
return $this->belongsToMany('App\Tag')->withTimestamps();
}
}
Here is my Tag class relationship:
class Tag extends Model {
public function articles() {
return $this->belongsToMany('App\Articles');
}
}
Here is my migration for the pivot table:
public function up() {
Schema.create('article_tag', function(Blueprint $table) {
$table->integer('article_id')->unsigned()->index();
$table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
$table->integer('tag_id')->unsigned()->index();
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->timestamps();
});
}
Can I add to the pivot table migration $table->boolean('is_green_tag')->nullable()?
Yes you can, you can give it a default of 0 instead of making it nullable:
$table->boolean('is_green_tag')->default(0);
And then you can modify the relationship on Article class:
public function tags() {
return $this->belongsToMany('App\Tag')->withPivot(['is_green_tag'])->withTimestamps();
}
Once you have an Article object, you can access that property:
foreach ($article->tags as $tag) {
if ($tag->pivot->is_green_tag) {
// some logic here
}
}
To save is_green_tag for a $tagId:
$article->tags()->attach($tagId, ['is_green_tag' => 1]);
Laravel docs:
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many
https://laravel.com/docs/5.7/eloquent-relationships#updating-many-to-many-relationships
I use this table schema:
Schema::create('forms', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 255)->default('');
$table->text('html')->nullable();
$table->text('json')->nullable();
$table->timestamps();
$table->softDeletes();
});
This is the model:
class Form extends Model
{
use SoftDeletes;
protected $fillable = [
'name',
'html',
'json'
];
protected $hidden = ['created_at', 'updated_at', 'deleted_at'];
}
And in the controller I want to show a list of all items of model but only the id and name fileds. Now I use this, but it show all not hidden fields:
public function index() {
return Form::->paginate(100);
}
This function is only for the list of forms names. But here is the second one for show a form datas for modify:
public function show(string $id) {
$item = Form::findOrFail($id);
return response()->json($item);
}
Of course this last one function needs to be show all fields (id, name, html and json too).
Is there any best practice to show only fields what I needed in the index() function using with paginate()?
If i am not wrong then hopefully you can do it something like this for getting specific columns along with pagination:
return Form::paginate(100,['id','name',.....]);
If I read your question correctly, what you want to do is create a collection of the Form object where only the id and the name fields are actually retrieved on the index overview.
You can do that pretty easily by creating a new collection instance in your controller:
public function index() {
// use the Eloquent select() function
$forms = Form::select('id', 'name')->paginate(100);
return $forms;
}
I would personally put that collection in a repository pattern to make it more easily cacheable. Here's a nice canonical reference to repository patterns in Laravel.
In your show function on your controller you needn't change a thing, considering the ID is still the same.
For future reference, remember that the paginate method paginates only the collection it is called on, and not everything related to a specific model or anything other than that collection. Thus, if you create a new collection in any way, and call the paginate method on that new collection, only whatever is inside that will be paginated. It's pretty powerful stuff! Here's the documentation reference.
I have an eloquent many to many relationship and I want to use attach() to easily create role_permissions data but the problem is I'm using an UUID for my ID and it throws an error Field 'id' doesn't have a default value. Any way of hijacking the attach() method? so I can set my UUID?
My migration
Schema::create('role_permissions', function (Blueprint $table) {
$table->increments('count')->unique();
$table->string('id')->unique();
$table->string('role_id');
$table->string('permission_id');
$table->timestamps();
});
My model
class Role extends Model
{
//
public $incrementing = false;
public function users()
{
return $this->belongsToMany('App\User', 'user_roles', 'role_id', 'user_id');
}
public function permissions()
{
return $this->belongsToMany('App\Permission', 'role_permissions', 'role_id', 'permission_id');
}
}
My attach code
$role->permissions()->attach($permission_ids);
I know the problem here is that my id is not an incrementing number it's an unique string. My question is how do I "Inject" that unique string to the attach() method? Thank you guys.
The error
Field 'id' doesn't have a default value
refers to the fact that your database does not know how to fill the id field when it's not specified.
Either you edit the schema adding a nullable:
Schema::create('role_permissions', function (Blueprint $table) {
$table->increments('count')->unique();
$table->string('id')->unique()->nullable(); // Bad idea
$table->string('role_id');
$table->string('permission_id');
$table->timestamps();
});
or injecting it via attach:
$role->permissions()->attach($permission_ids, ["id" => null]);
More info on Laravel official doc
Update
For the future developers who encounter this problem you can also set anything inside the attach array, for example:
$role->permissions()->attach($permission_ids, ["id" => Uuid::generate()]);
Update 2
There's also a more clean way to handle this to be honest. I will try to explain it.
You can handle the Pivot events inside the event service provider by simply hooking into the bootmethod:
Here's a snippet
/App/Providers/EventServiceProvider.php
public function boot()
{
Pivot::creating(function($pivot) {
if ($pivot->getTable() == 'role_permissions') {
$pivot->id = Uuid::generate();
}
});
}
Be aware I do not know if this is actually possible on your laravel version. Mine (5.4.*) works as intended
Okay managed to fixed it, what I did with the help of #Claudio Ludovico Panneta's tip.
foreach($permission_ids as $permission_id)
{
$role->permissions()->attach($permission_id, ["id" => Uuid::generate()]);
}