I have a menu in my Laravel application which lists tags. I want to be able to add more tags and change their order, but I don't want to affect the underlying database ids for each tag. Because that would break any data I have already associated with them. So I thought I add an extra column to my database called 'display_order' like so:
Database:
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->integer('display_order');
$table->string('name');
$table->timestamps();
});
Now I want to create a view to let the user change the value of the display_order field. Initially I show a page which prints each tag and its display_order and allows the user to update the value in a text field:
Controller:
public function showTagsOrder()
{
$tags = Tag::all()->sortBy('display_order');
return view('menu.tags', compact('tags'));
}
Route:
Route::get('/menu/tags', ['as' => 'menu_tags', 'uses' => 'MenuOrderController#showTagsOrder']);
View:
{!! Form::open(['action' => 'MenuOrderController#updateTagsOrder']) !!}
#foreach($tags->chunk(18) as $chunk)
<div class="col">
#foreach($chunk as $tag)
<p><label>{!! $tag->name !!}</label><input type="text" name="tag_array[{!! $tag->id !!}]" value="{!! $tag->display_order !!}" class="numeric" />
#endforeach
</div>
#endforeach
<p>{!! Form::submit('Submit') !!}
{!! Form::close() !!}
When the form is submitted I then have the following function in my controller:
public function updateTagsOrder(TagsMenuRequest $request)
{
$i = 1;
foreach($request->tag_array as $tag_value)
{
$tag = new Tag;
$tag = Tag::find($i);
$tag->display_order = $tag_value;
$tag->save();
$i++;
}
flash()->overlay('The order of tags in the menu has been udpated!', 'Congratulations');
return redirect('/');
}
But this doesn't work. How would I rewrite this so I assigned the correct value for display_order to each tag. I'd be very grateful for your help.
foreach($request->tag_array as $tag_id => $tag_value)
{
$tag = Tag::find($tag_id);
$tag->display_order = $tag_value;
$tag->save();
}
Related
I am wondering if I can query a hasOne -> hasMany relationship in blade. I can currently get a count on how many models exist in my blade using "$participant->messages->count()" but I would like to check the model and count other things. For instance I would like to run the following query in blade:
{!! $participant->messages->where($this->messages->mediaURL, "=", null)->count() !!}
I get the following error:
Property [mediaURL] does not exist on this collection instance.
Here is my controller function
public function showParticipants()
{
$participants = Participant::all();
// $messages = $participants->participant_id->messages;
return view('home')->with(['participants'=> $participants, 'messages'=>'hi']);
}
Part of my Participant model:
public function messages()
{
return $this->hasMany('App\Message', 'message_id', 'participant_id');
}
Part of my Message model:
public function participant()
{
return $this->belongsTo(Participant::class);
}
My message table structure:
public function up()
{
Schema::create('messages', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('message_id')->unsigned();
$table->string('message_content')->nullable();
$table->string('mediaSID')->index()->nullable();
$table->string('messageSID')->index()->nullable();
$table->string('mediaURL')->index()->nullable();
$table->binary('media')->nullable();
$table->string('filename')->index()->nullable();
$table->string('MIMEType')->nullable();
$table->timestamps();
});
Schema::table('messages', function($table) {
$table->foreign('message_id')->references('participant_id')->on('participants')->onDelete('cascade');
});
}
My Participant DB structure:
public function up()
{
Schema::create('participants', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->string('participant_id')->unique();
$table->dateTime('appointmentDate')->nullable();
$table->dateTimeTz('timezoneOffset')->nullable();
$table->dateTime('appointmentDate_twoWeeks')->nullable();
$table->dateTime('notificationTime')->nullable();
$table->integer('notificationTally')->nullable();
$table->boolean('studyCompleted')->default(0);
$table->boolean('subscribed');
$table->timestamps();
});
}
My blade just to give all info:
#isset($participants)
#foreach ($participants as $participant)
<tr>
<td>
{!! $participant->participant_id !!}
</td>
<td>
{!! $participant->subscribed !!}
</td>
<td>
{!! $participant->notificationTime !!}
</td>
<td>
{!! $participant->notificationTally !!}
</td>
<td>
{!! $participant->studyCompleted !!}
</td>
<td>
{!! $participant->messages->count() !!}
</td>
<td>
{!! $participant->messages->where($participant->messages->mediaURL, "=", null)->count() !!}
</td>
</tr>
#endforeach
#endisset
I believe the issue is $this->messages->mediaURL. With the Query Builder, if you're wanting to refer to a column on a table you would just need to pass it a string. Also when you're querying a relationship you should use the method and not the property e.g. $participant->messages(). Lastly, when querying for a null column you can use the whereNull method.
{!! $participant->messages()->whereNull('mediaURL')->count() !!}
You are using the "magic" __get() on messages that returns a collection because it is a has many relationship.
$participant->messages->where($this->messages->mediaURL, "=", null)->count()
should be
$participant->messages()->where($participant->messages()->first()->mediaURL, "=", null)->count()
I am trying to run a search query on all of my comments. I am encountering a number of problems. I would to have two search queries one that runs a search on the comment ID and another that runs a search on the username that is connected to the comment through the user_id FK.
Currently im getting the problem:
Too few arguments to function Illuminate\Support\Collection::get(), 0 passed in
Tables:
Comment- id, comment, user_id, timestamps
User - id, name, username
Models:
class Comment extends Model
{
public function author()
{
return $this->belongsTo('App\User','user_id');
}
}
Controller:
public function index(Request $request)
{
$comment =Comment::paginate(10);
$id=$request->input('id');
$name=$request->input('username');
if(!empty($id)){
$comment->where('id', $request->input('id') )->get();
}
if(!empty($name)){
$comment->where($comment->author->username, 'LIKE', '%'.$name.'%')->get();
}
return view('comments.index')->withComment($comment);
}
View:
<div class="panel-body">
{!! Form::open(['route' => 'comments.index', 'method' => 'GET']) !!}
<div class="col-md-5">
{!! Form::label('id', 'Search By ID:') !!}
{!! Form::text('id', null, array('class' => 'form-control')) !!}
</div>
<div class="col-md-5">
{!! Form::label('username', 'Search By Username:') !!}
{!! Form::text('username', null, array('class' => 'form-control')) !!}
</div>
<div class="col-md-2">
{!! Form::submit('Find Comments', array('class' => 'btn btn-send ')) !!}
</div>
{!!Form::close()!!}
</div>
#foreach($comment as $comments)
//data
#endforeach
The paginate function immediately executes the query for you, so you are using Collection functions after that. The get function on collections expects a key as a parameter, this is the problem.
To fix this, you can either remove the ->get() or use the paginate function at the end of your query as shown below.
$comment = Comment::query();
$id = $request->input('id');
$name = $request->input('username');
if (!empty($id)) {
$comment = $comment->where('id', $request->input('id'));
}
$result = $comment->paginate(10);
Since You have Paginated already
public function index(Request $request)
{
$comment = new Comment();
$id=$request->input('id');
$name=$request->input('username');
if(!empty($id)){
$comment->where('id', $request->input('id') )->get();
}
if(!empty($name)){
$comment->where($comment->author->username, 'LIKE', '%'.$name.'%');
}
return view('comments.index')->withComment($comment->paginate(10));
}
You cannot paginate and get it again, its like using SELECT statement twice on same object.
I have these 2 tables with many to many relationship connected using a junction table. The idea is that I can get the user data to make the user an author in a journal data and it works so far.
User table :
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->integer('phone')->nullable();
$table->string('address')->nullable();
$table->string('password');
$table->rememberToken();
$table->enum('level', ['admin', 'author']);
$table->timestamps();
});
}
Journal table :
public function up()
{
Schema::create('journal', function (Blueprint $table) {
$table->increments('id');
$table->string('title', 255);
$table->text('abstract');
$table->text('file');
$table->integer('id_edition')->unsigned();
$table->timestamps();
});
}
Junction table :
public function up()
{
Schema::create('penulis', function (Blueprint $table) {
// Create tabel penulis
$table->integer('id_user')->unsigned()->index();
$table->integer('id_journal')->unsigned()->index();
$table->timestamps();
// Set PK
$table->primary(['id_user', 'id_journal']);
// Set FK penulis --- user
$table->foreign('id_user')
->references('id')
->on('users')
->onDelete('cascade')
->onUpdate('cascade');
// Set FK penulis --- journal
$table->foreign('id_journal')
->references('id')
->on('journal')
->onDelete('cascade')
->onUpdate('cascade');
});
}
Now I have this view that shows journals data along with the buttons to edit or delete it. What I want to make is that only the user that are entitled as the author of the journal that has the capacity to access these buttons. How do I make it ? below is the view code :
<tbody>
<?php foreach ($journal_list as $journal): ?>
<tr>
<td style="">{{ $journal->title }}</td>
#if (Auth::check())
<td style="width: 130px; overflow: hidden;">
<div class="box-button">
{{ link_to('journal/' . $journal->id . '/edit?edition=' . $edition->id, 'Edit', ['class' => 'btn btn-warning btn-sm']) }}
</div>
<div class="box-button">
{!! Form::open(['method' => 'DELETE', 'action' => ['JournalController#destroy', $journal->id]]) !!}
{!! Form::submit('Delete', ['class' => 'btn btn-danger btn-sm']) !!}
{!! Form::close() !!}
</div>
</td>
#endif
</tr>
<?php endforeach ?>
</tbody>
Sorry for my bad English and if my question is stupid. Thanks!
You need to use a combination of middleware and Gate facade.
Generate a policy
Write a policy
Like this:
public function edit-journal(User $user, Journal $journal)
{
return $user->id === $journal->user_id;
}
public function delete-journal(User $user, Journal $journal)
{
return $user->id === $journal->user_id;
}
3. You can now use the Gate facade with blade
Like this:
#can('edit-journal', $journal)
<div class="box-button">
{{ link_to('journal/' . $journal->id . '/edit?edition=' . $edition->id, 'Edit', ['class' => 'btn btn-warning btn-sm']) }}
</div>
#endcan
#can('delete-journal', $journal)
<div class="box-button">
{!! Form::open(['method' => 'DELETE', 'action' => ['JournalController#destroy', $journal->id]]) !!}
{!! Form::submit('Delete', ['class' => 'btn btn-danger btn-sm']) !!}
{!! Form::close() !!}
</div>
#endcan
You will have to register a middleware for your edit and delete route. Your routes should look like:
//Routes
Route::get('journal/' . {$journal_id} . '/edit', ['as'=>'editJournal','middleware' => 'journal:edit', 'uses'=>'JournalController#edit']
//You need to change your delete form so the action points to that route
Route::delete('journal/' . {$journal_id}, ['as'=>'deleteJournal','middleware' => 'journal:delete', 'uses'=>'JournalController#destroy']
In your middleware, you should have something like:
//Journal Middleware
public function handle($request, Closure $next, $role)
{
$parameters = $request->route()->parameters();
$journal = Journal::findOrFail($parameters['journal_id']);
if (Gate::allows($role.'-journal', $journal)) {
return $next($request);
}else{
abort(403, "You do not have the permission to ".$role." this journal")
}
}
What I;ve done in similar cases is to add a function inside the model that you want to check with all the logic. So for example in your case would be something like:
/Model/Journal.php
public function canBeModifiedByUser($user_id){
//Check all the things that you want
}
Then in the view you can do something like:
if($journal->canBeModifiedByUser($journal->user->id))
Also I would suggest you to check some ACL packages, it might be an overkill for you atm but it might just be what you need.
I would suggest using gates
in your auth service provider you can do
$gate->define('can-modifiy', function ($user) {
// whatever code you want to determine if the user can eg
return $user->hasRole('admin');
});
then in your views you can use #can
#can ('can-modify')
<button>delete</button>
#endcan
This can also be used in your controllers with
$this->authorize('can-modify');
or
Gate::allows('can-modify');
This is in the docs at https://laravel.com/docs/5.3/authorization#writing-gates
So first of all I just started to work with laravel and overall with php. Right now I'm facing a problem where I don't know how to display specific video from my database. My User model:
class User extends Model implements Authenticatable{
use \Illuminate\Auth\Authenticatable;
public function videos() {
return $this->hasMany('App\Video');
}
}
My Video model:
class Video extends Model{
public function user() {
return $this->belongsTo('App\User');
}
}
Everything goes well when I loop through my videos to display them all in dashboard:
<div class="row" id="features">
#foreach($videos as $video)
<div class="col-sm-4 feature">
<div class="panel">
<div class="panel-heading">
<h3 class="panel-title video_name">{{ $video->video_name }}</h3>
</div>
<iframe width="320" height="250"
src="https://www.youtube.com/embed/{{ $video->video_url }}" allowfullscreen="allowfullscreen" mozallowfullscreen="mozallowfullscreen" msallowfullscreen="msallowfullscreen" oallowfullscreen="oallowfullscreen" webkitallowfullscreen="webkitallowfullscreen">
</iframe>
<div class="info">
<p>Posted by {{ $video->user->first_name }} on {{ $video->created_at }}</p>
<hr class="postInfo">
</div>
<p>{{ $video->description }} </p>
Continue to video
</div>
</div>
#endforeach
</div>
But At this point:
Continue to video
I open new route which is (http://localhost:8000/video/11/view) and in this case I want to display video where ID 11 is equal to my video_url
videos table code:
public function up(){
Schema::create('videos', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->text('video_name');
$table->text('video_url');
$table->text('description');
$table->integer('user_id');
});
}
Route:
Route::get('/video/{video_id}/view', [
'uses' => 'SignInController#ViewVideo',
'as' => 'view.video']);
Change the route to following..
Route::get('/video/{video}/view', [
'uses' => 'SignInController#ViewVideo',
'as' => 'view.video']);
In Controller..
public function ViewVideo(Video $video){
//any authorization logic...
return view('whatever',compact('video'));
}
As of Laravel 5.2 there is a thing called Implicit Route Model Binding You can read the docs about it here.
So in your example. You can change your route like this:
Route::get('/video/{video}/view', [
'uses' => 'SignInController#ViewVideo',
'as' => 'view.video'
]);
And in your Video controller's view method:
public functin view(App\Video $video) {
// Your logic
}
I want to delete a record: remove a routine,that belongs to a user, on button click (hasMany). I have set up the view, models and relationship within,delete route, and the controller method to delete.
When I try to click the button to remove the routine from the db, it does nothing. why does it not removing the record?
Here's my code: route:
Route::post('routine/delete', 'RoutineController#delete'); // Delete a routine for a user.
Controller:
public function delete(Request $request)
{
$id = $request->input("id"); // Getting the id via. the ajax request.
$routine = \App\Routine::find($id); //Fetching the routine object from the db ifentified by the id passed via ajax
if ($routine)
{
$routine->delete();
}
return ["status" => "success"];
}
View:
<div class="col-lg-2">
<!-- When this button is clicked, we determine which routine to remove. -->
<button class="btn btn-danger remove_routine" data-id="{{$routine->id}}" data-token="{{csrf_token()}}" style="display:inline">Delete</button>
</div>
User Model:
public function routine()
{
return $this->hasMany('App\Routine');
}
Routine model:
public function user()
{
return $this->belongsTo('App\User');
}
Thanks in advance!
Don't know if it exactly answers your question, and I don't use AJAX, but I always do my deletes like this:
View
#foreach($database-thing as $item)
<form method="POST" action="$yourActionHere" style="display:inline" >
<input name="_method" type="hidden" value="DELETE">
<button type="submit" class="btn btn-danger btn-xs"><i class="fa fa-trash"></i> Delete</button>
</form>
#endforeach
// Even easier with laravelcollective/forms
#foreach($database-thing as $item)
{!! Form::open([
'method'=>'DELETE',
'url' => [$yourUrl, $item->id // <-- Very important],
'style' => 'display:inline'
]) !!}
{!! Form::button('<i class="fa fa-trash"></i> Verwijder', ['type' => 'submit', 'class' => 'btn btn-danger btn-xs']) !!}
{!! Form::close() !!}
#endforeach
Controller
public function destroy($id)
{
YourModel::destroy($id);
// All your other logic like redirects and stuff
}
Working delete, based on the code above and this updated controller function:
public function delete(Request $request,$id)
{
$user=Auth::user();
$routine = \App\Routine::findOrFail($id); // find or throw an error if you don't find the routine id in db.
// Makes if() statement redundant, since it checkes for that id already.
// Need to check that the owner of the routine is the current authenticated user.
if ($user->id != $routine->user->id)
{
Session::flash('flash_message', 'No routine found.');
}
else
{
$routine->delete();
Session::flash('routine_deleted','Routine deleted!');
}
// No need to return $routine since I am deleting it, otherwise it will throw and error of trying to get property of non-object.
return redirect()->back()->with('user') ;
//return view('view_routine')->with('user', 'routine');
}
There you go
$remove = 2;
$filtered = $c->filter(function ($value, $key) use($remove){
return $value['id']!=$remove;
});