Changing menus order by dragging - Laravel - laravel

In my custom CMS I have created CRUD menu and in the frontend I'm displaying it successfully.
Now I'm trying to find any solution to change order by dragging these menu items in the admin section, but still no luck. Does anyone knows how to achieve this with Laravel? Also I use third party Laravel translatable, so I'll explain in my following code:
Menu model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Dimsav\Translatable\Translatable;
class Menu extends Model
{
use Translatable;
public $translatedAttributes = ['title', 'url', 'status'];
}
MenuTranslation model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class MenuTranslation extends Model
{
//
}
create_menus_table migration:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenusTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('menus', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('menus');
}
}
create_menus_translations_table migration:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenuTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('menu_translations', function (Blueprint $table) {
$table->increments('id');
$table->integer('menu_id')->unsigned();
$table->string('locale')->index();
// The actual fields to store the content of your entity. You can add whatever you need.
$table->string('title');
$table->string('url')->nullable();
$table->string('status');
$table->unique(['menu_id', 'locale']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('menu_translations');
}
}
My view index menu:
#extends('layouts.admin')
#section('content')
#if(session()->has('success_message'))
<div class="alert alert-success">
{{ session()->get('success_message') }}
</div>
#endif
<h1>Users</h1>
<a class="btn btn-primary" href="{{ route('menus.create') }}">Create new menu</a>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Status</th>
<th>Created</th>
<th>Operations</th>
</tr>
</thead>
<tbody>
#foreach($menus as $menu)
<tr>
<td>{{ $menu->id }}</td>
<td>{{ $menu->title }}</td>
<td>{{ $menu->status ? 'Active' : 'Disabled' }}</td>
<td>{{ $menu->created_at->diffForHumans() }}</td>
<td>
<a class="btn btn-primary btn-edit" href="{{ route('menus.edit' , $menu->id) }}">Edit</a>
<form action="{{ route('menus.destroy', $menu->id) }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<input type="submit" class="btn btn-danger btn-delete" value="Delete">
</form>
</td>
</tr>
#endforeach
</tbody>
</table>
#endsection
As you can see in the view menu index I'm displaying the created menu items. The key is to use some js and Ajax I guess and with dragging to change the id's in the menus table or to create a new column in the database (order).
So, the question again is: how can I drag these menu items and change order in the database by using Laravel?

Well, it is all not laravel specific question but here is the answer.
So, For the frontend part, you'll need to use a plugin for Drag and Drop. E.g. Sortable
Sortable plugin gives you all the events of drag and drop. So you can simply create a hidden field or just create a variable in the javascript and pass that sorted value in the POST request when click on save function.
Now, you will need to add that data in a table sorted by menu_id.
And by clicking on save order, you will send all the newly ordered elements with their respective id.
And on the backend side, you will just need to loop through and update those elements.
Like this:
foreach($newSort as $key => $value):
$menuTranslation = MenuTranslation::findOrFail($key);
$menuTranslation->update(['menu_id' => $value]);
#endforeach;
All the best.

I've finally solved the problem!
I'm using this plugin for Laravel:
https://github.com/boxfrommars/rutorika-sortable
It's great for sorting eloquent. With some tweaks, lot of reading and testing, now it works great. :)

Related

How to return Model in Many to Many Polymorphic Associations in Laravel

I am able to successfully create the relationships, but I now want to identify what the model used in the relationship is, but I don't know how to access it. Could someone point me in the right direction? I have tried the following down below in the index.blade.php file, but it just returns NULL
1 Possible solution is to check to see if the value is empty on each relationship definition each time, but is there a laravel built in way to check?
#if ($event->services->isNotEmpty())
There are services!
#endif
Basically I have two relationships defined courses and services. I want to check which 1 each event is.
I have the following relationships:
services
courses
events
schedulables
event_user
Events.php model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function users()
{
return $this->belongsToMany('App\User');
}
public function courses()
{
return $this->morphedByMany('App\Course', 'schedulable');
}
public function services()
{
return $this->morphedByMany('App\Service', 'schedulable');
}
}
index.blade.php
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Start</th>
<th scope="col">End</th>
<th scope="col">Assigned</th>
<th scope="col">Type</th>
<th scope="col">Options</th>
</tr>
</thead>
<tbody>
#foreach ($events as $event)
<tr>
<th scope="row">{{$event->start_date}}</th>
<td>{{$event->end_date}}</td>
<td>{{$event->user->name}}</td>
<td>{{Illuminate\Database\Eloquent\Relations\Relation::getMorphedModel($event->getMorphClass())}}
</td>
<td>
<form action="{{route('events.destroy', $event)}}" method="POST">
#csrf
#method('DELETE')
<button type="submit" class="btn btn-outline-danger">Delete</button>
</form>
</td>
</tr>
#endforeach
</tbody>
</table>
According to the documentation, this piece of code only apply to a $service or $course model:
Illuminate\Database\Eloquent\Relations\Relation::getMorphedModel($event->getMorphClass())
You can simply define a type custom attribute inside the Event model:
use Illuminate\Database\Eloquent\Relations\Relation;
class Event
{
public function getTypeAttribute()
{
return class_basename($this->schedulable_type);
}
}
Then within the blade file, you can simply say:
<td>{{$event->type}}</td>

Laravel - Method App\Http\Controllers\MsisdnController::export does not exist

I am trying to export to Excel in Laravel 5.8 using
"maatwebsite/excel": "^3.1"
but I got this error:
Method App\Http\Controllers\MsisdnController::export does not exist.
Exports
namespace App\Exports;
use App\User;
use Maatwebsite\Excel\Concerns\FromCollection;
class MsisdnExport implements FromCollection
{
/**
* #return \Illuminate\Support\Collection
*/
public function collection()
{
return User::all();
}
}
MsisdnController
use App\Exports\MsisdnExport;
use Maatwebsite\Excel\Facades\Excel;
class MsisdnController extends Controller
{
public function msisdnexport()
{
return Excel::download(new MsisdnExport, 'msisdn.xlsx');
}
}
Sidebar Nav
<li>List of MSISDNs </li>
route
Route::get('msisdnexport', 'MsisdnController#export')->name('msisdnexport');
View
<div class="col-xs-4">
{{ Form::submit('Search',['class'=>'btn btn-warning']) }}
<i class="fa fa-file-excel-o"></i> Excel
</div>
#foreach($msisdns as $key => $msisdn)
<tr>
<td>{{ ++$key }}</td>
<td>{{ $msisdn->phone }}</td>
</tr>
#endforeach
When I click on Excel Button, I expect it to export to Excel. But I got the error above.
Your route definition:
Route::get('msisdnexport', 'MsisdnController#export');
references a function export that doesn't exist. Your function is named msisdnexport in your controller.

Laravel view won't loop model even though it is populated

I'm creating a web based interface for my dovecot database.
I can get a list of all the virtual domains in the database and the number of emails and aliases easily enough.
But when I try to load a page to list the emails under a specific domain, it goes weird.
Three simple models:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VirtualDomain extends Model
{
public function emails()
{
return $this->hasMany('App\VirtualUser', 'domain_id');
}
public function aliases()
{
return $this->hasMany('App\VirtualAlias', 'domain_id');
}
}
class VirtualUser extends Model
{
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
];
}
class VirtualAlias extends Model
{
//
}
My default controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\VirtualDomain;
use App\VirtualUser;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('home', [
'domains' => VirtualDomain::all(),
]);
}
public function domain($name)
{
$domain = VirtualDomain::where('name', $name)->first();
return view('domain', [
'domain' => $domain,
'emails' => VirtualUser::where('domain_id', $domain->id),
]);
}
}
and a couple of simple blades
home.blade.php
<p>
{{ $domains->count() }} domains
</p>
<table class="table-summary">
<thead>
<tr>
<th>name</th>
<th>emails</th>
<th>aliases</th>
<th class="table-summary-options">options</th>
</tr>
</thead>
<tbody>
#foreach ($domains as $domain)
<tr>
<td><a title="view emails for this domain" href="domain/{{ $domain->name }}">{{ $domain->name }}</a></td>
<td>{{ $domain->emails->count() }}</td>
<td>{{ $domain->aliases->count() }}</td>
<td class="table-summary-options"><a class="ui-action" title="remove this domain" href=""><img src="/img/ui/remove.png" alt="remove"></a></td>
</tr>
#endforeach
</tbody>
</table>
and domain.blade.php
<p>
<< - {{ $domain->name }} - {{ $emails->count() }} emails
</p>
<table class="table-summary">
<thead>
<tr>
<th>email</th>
<th class="table-summary-options">options</th>
</tr>
</thead>
<tbody>
#foreach ($emails as $email)
<tr>
<td><a title="view aliases for this domain" href="email/{{ $email->email }}">{{ $email->email }}</a></td>
<td class="table-summary-options"><a class="ui-action" title="remove this email" href=""><img src="/img/ui/remove.png" alt="remove"></a></td>
</tr>
#endforeach
</tbody>
</table>
The view outputs the correct number of emails under the domain with {{ $emails->count() }} - but the#foreach ($emails as $email)` does not loop.
When I modify the blade to simple use the emails from the domain variable ({{ $domain->emails->count() }} and #foreach ($domain->emails as $email)), I get the right count and the list is populated correctly.
What's making it go wrong when using the emails variable?
You have to make a small change for it to work
public function domain($name)
{
$domain = VirtualDomain::where('name', $name)->first();
return view('domain', [
'domain' => $domain,
'emails' => VirtualUser::where('domain_id', $domain->id)->get(),
]);
}
Without ->get() you will get a query builder instance while with get() will return a collection. In the foreach loop a collection can be iterated while a query builder instance can't be.
Hope this helps

Many to Many form binding

How to binding a input with many to many relationship data?
My relationship is: a Model has many Damages, and a Damage has many Models. in pivot table exists a price field.
I need populate a input with price data.
{{ Form::input('number', "prices[{$model->id}][{$damage->id}]") }}
My Model:
class Model extends \BaseModel {
public function damages()
{
return $this->belongsToMany('Damage', 'prices_damages', 'model_id', 'damage_id')
->withPivot('price')
->withTimestamps();
}
}
Pivot table
Schema::create('prices_damages', function(Blueprint $table)
{
$table->increments('id');
$table->integer('model_id')->unsigned();
$table->integer('damage_id')->unsigned();
$table->float('price')->nullable();
$table->timestamps();
});
Controller
/**
* Display a index dashboard page.
*
* #return \Illuminate\Http\Response
*/
public function getDamages()
{
$models = \Model::orderBy('order')->get();
$damages = \Damage::orderBy('order')->get();
return $this->render('Avarias', 'prices.damages', compact('models', 'damages'));
}
View:
<table class="table-striped table-header-rotated">
<thead>
<tr>
<th></th>
#foreach ($damages as $damage)
<th class="vertical"><div><span>{{ $damage->name }}</span></div></th>
#endforeach
</tr>
</thead>
<tbody>
#foreach ($models as $model)
<tr>
<td>{{ $model->fullname }}</td>
#foreach ($damages as $damage)
<td>
{{ Form::input('number', "prices[{$model->id}][{$damage->id}]", null, ['min' => 0, 'step' => 0.01]) }}
</td>
#endforeach
</tr>
#endforeach
</tbody>
</table>
You can't bind a collection (in the sense of laravel form model binding), so you can do this:
#foreach ($model->damages as $damage)
{{ Form::input('number', "damages[{$damage->id}][price]", $damage->pivot->price) }}
#endforeach

Accessing and displaying pivot table data

Theory:
Users can attend many events and many events can be attended by many users. Therefore, I have two many-to-many relationships within my models, linking to a pivot table (event_user). On attending each event, I want to be able to access the pivot table data (event_user) to see if they're already attending.
event_user:
--id
--event_id
--user_id
For example, my seed data is:
user 1 attending both event 2 and 3. I want to be able to show this within a view.
The closest I have got is (logically):
public function index()
{
$id = Auth::user()->id;
$attending = myApp\Event::find($id)->event;
var_dump($attending); **But this var_dump returns NULL, but $id returns the correct ID.**
$events = myApp\Event::all();
$this->layout->content = View::make('events.index')->with('events', $events);
}
My aim is to disable the 'attend' button, on any event where they are already attending, only leaving the attend-able events available!
Any help would be hugely appreciated. Thank you in advance.
Any additional code which you may find necessary:
Events Model:
<?php
namespace myApp;
use Eloquent;
class Event extends Eloquent {
public function consultant()
{
return $this->belongsToMany('myApp\Consultant');
}
public function location()
{
return $this->belongsToMany('myApp\Location');
}
public function user()
{
return $this->belongsToMany('myApp\User');
}
}
User Model:
<?php
namespace myApp;
use Eloquent;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
public function event()
{
return $this->belongsToMany('Event');
}
public function practice()
{
return $this->belongToMany('Practice');
}
index.blade.php (showing the event list)
<div class="panel panel-success">
<!-- Default panel contents -->
<div class="panel-heading"><strong>Events</strong></div>
<!-- <div class="panel-body">
</div> -->
<!-- Table -->
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Presenter</th>
<th>Location</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach($events as $event)
<tr>
<td>{{ $event->title }}</td>
<td>{{ date("j F Y", strtotime($event->date)) }}</td>
<td>{{ $event->consultant()->first()->title }} {{ $event->consultant()->first()->surname }}</td>
<td>{{ $event->location()->first()->address_1 }}</td>
<td><button type="button" class="btn btn-info">Attend</button></td>
</tr>
#endforeach
</tbody>
</table>
</div>
</table>
I think you're going about this the wrong way, that or I have misunderstood.
Firstly, you're trying to find an event with the same primary key as the currently authenticated user, which isn't correct, although it's an easy hole to fall down.
$id = Auth::user()->id;
$attending = myApp\Event::find($id)->event;
// equivalent of: SELECT * FROM `events` WHERE `id` = ?
Instead you'll want to do this
$id = Auth::user()->id;
$attending = myApp\Event::where('user_id', $id)->get();
// equivalent of: SELECT * FROM `events` WHERE `user_id` = ? (user_id insted of events.id)
That being said, surely the user events can be accessed by just calling the event property on the auth user?
$user = Auth::user();
$attending = $user->event;
To take this one step further, and make it so that you can check inside the foreach loop, you could advanced the above code to look like the following
$user = Auth::user();
$attending = $user->event->lists('id');
This will make an array of ids from the returned events that you need to assign to the view
$this->layout->content = View::make('events.index', array('events' => $events, 'attending' => $attending));
Now you can freely access it in your foreach
#foreach($events as $event)
<tr>
<td>{{ $event->title }}</td>
<td>{{ date("j F Y", strtotime($event->date)) }}</td>
<td>{{ $event->consultant()->first()->title }} {{ $event->consultant()->first()->surname }}</td>
<td>{{ $event->location()->first()->address_1 }}</td>
<td>
#if (!in_array($event->id, $attending))
<button type="button" class="btn btn-info">Attend</button>
#endif
</td>
</tr>
#endforeach
Also, seeing as Event is a reserved Alias (unless you've modified the config, which I wouldn't recommend), you'll want to specify the namespace in the relationship declaration within User
public function event()
{
return $this->belongsToMany('myApp\Event');
}
As a final point, it's not an issue as such, but in my own code I try to name relationships that have the potential to return multiple objects in the plural, so it would be public function events();.

Resources