laravel POST request not rendering - laravel

Below is the auth part of my routes.
I just added the Tags part (where I can add another tag to the DB).
the tag creation works but the creation of a new post doesn't work now (worked before).
When I "submit" a post, it doesn't redirect or submits anything and it refreshes me back to the post create form with empty fields like nothing was rendered.
I tried to play with the positions of the routing, I made the post creation work but than the same happened to the tag creation where the page was "submiting" but actually there was no submit and it didn't redirect afterwards.
Auth::routes();
Route::get('/posts', 'PostsController#index')->name('posts.index');
Route::middleware('can:isAdmin')->group(function () {
Route::get('/posts/create', 'PostsController#create')->name('posts.create');
Route::get('/posts/{post}/edit', 'PostsController#edit')->name('post.edit');
Route::put('/posts/{post}', 'PostsController#update');
Route::post('/posts', 'PostsController#store');
Route::get('/tags/create', 'TagsController#create')->name('tags.create');
Route::post('/posts', 'TagsController#store');
});
Route::get('/posts/{post}', 'PostsController#show')->name('posts.show');
thanks in advance.

At first, in given configuration you have two routes for same method / URI combination, so one of them would be unreachable:
// here the first
Route::post('/posts', 'PostsController#store');
Route::get('/tags/create', 'TagsController#create')->name('tags.create');
Route::post('/posts', 'TagsController#store'); // <-- here the second
Looks like your post form submit goes to the tags, than validation fails and it redirect you back to post create page. Do you display validation errors?
here is example - https://laravel.com/docs/5.8/validation#quick-displaying-the-validation-errors
<!-- /resources/views/post/create.blade.php -->
<h1>Create Post</h1>
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
<!-- Create Post Form -->

If it refresh your page so probably it works but if it do not save your request to databse it means that your request not validate respect to table.
Use validation for understand the errors.

Related

Laravel pagination with URL parameter

I have a Laravel application. One of the pages can be reached via the following URL
http://localhost:8000/items/gallery?item_type=glasses
As the amount of items to be shown can be quite substantial, I'm using pagination. I have the following code in my view:
#foreach($media as $media_item)
<div class="col-md-3">
<div class="card">
<img class="card-img-top" src="{{ asset('storage/'.$media_item->id .'/'. $media_item->file_name) }}" ">
</div>
</div>
#endforeach
{{ $media->links() }}
and in the controller, I'm using:
$media = Media::paginate(5);
The pagination buttons are shown and work for the 1st one. Then when I click on the second (or third or fourth...) one, I get the following error message:
Method Illuminate\Database\Eloquent\Collection::links does not exist.
I see the link is trying to reach:
http://localhost:8000/beeritems/gallery?page=2
whereas I need:
http://localhost:8000/beeritems/gallery?item_type=glasses&page=2
In Laravel, how can I change the links() method to include the part after the question mark?
You must use ->appends() methods
$media = Media::paginate(5);
$media->appends($request->all());
you can use laravel basic URLs instead of getting gallery images with URL get parameters.
something like this:
define Route like this
/items/gallery/{types}
then using it like
http://localhost:8000/items/gallery/glasses
in this case you don't get that error anymore

Laravel: How to log errors in form request input

I am using Laravel's form request to validate some complex input. If there is an error, Laravel returns the user to the previous page (which is fine), but I also want it to log the errors for me. How do I do that?
I attempted to use the form's withValidator method as follows, but got an error re 'maximum nesting level reached'.
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($validator->fails()) {
foreach($validator->errors()->all() as $error) {
logger($error);
}
}
});
}
Calling $validator->fails() in the after callback will end in an endless loop as it calls $validator->after() in the process. It re-evaluates and does not use a previous result.
Use $validator->errors() instead and check if it is empty.
Best way to validate on laravel is to use Laravel Request
As mentioned in the Laravel Documentation,
If the incoming request parameters do not pass the given validation rules, Laravel will automatically redirect the user back to their previous location.
In addition, all of the validation errors will be present in the $error variable.
The $errors variable is bound to the view by the Illuminate\View\Middleware\ShareErrorsFromSession middleware, which is provided by the web middleware group. When this middleware is applied an $errors variable will always be available in your views, allowing you to conveniently assume the $errors variable is always defined and can be safely used.
To view the errors, you can add the following snippet inside your view which is returned when the validation is failed:
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif

Laravel - route("resource.destroy") calls "resource.show"

This is the web.php
Route::group(['middleware' => 'auth'],
function () {
Route::get('letters/getRows', 'LetterController#getRows')->name('letters.getRows');
Route::get('letters/{letter}/A4', 'LetterController#A4')->name('letters.A4');
Route::get('letters/{letter}/A5', 'LetterController#A5')->name('letters.A5');
Route::resource('letters', 'LetterController');
}
);
I created a link as follow
"<a class='mx-2 h5' href='".route('letters.destroy', $entity->id)."'><i class='icon-remove-circle'></i></a>".
where the $entity->id is the id of the letter. The problem is, it links to show method not the destroy method. What can I do?
Using a form like this
{{ Form::open(array('route' => array('letters.destroy', $entity->id), 'method' => 'delete')) }}
<button type="submit" >Delete Account</button>
{{ Form::close() }}
may solve the problem but I want to use a tag not a form.
update
In the php artisan route:list, the url of destroy and show are the same
thanks
When you use the Route::resource method it will create, among others, a route to DESTROY a resource like this: /letters/:id/ and another route to EDIT the resource: /letters/:id, and one more to SHOW /letters/:id
They all look the same. However, the difference is in the HTTP method/verb used to reach each route.
If you look to the output if php artisan route:list, you will find the list of HTTP methods used. Something like:
GET|HEAD | letters/{letter} | letters.show
PUT|PATCH | letters/{letter} | letters.update
DELETE | letters/{letter} | letters.destroy
Therefore, to show a letter, you use a GET method, to edit a letter, use PUT method, and to destroy/delete, you use a DELETE method.
When you use an a tag, the browser will use the GET method, thus will reach the letters.show route. Html forms, can use POST or GET. Finally to use the DELETE http method, you need a form with hidden input named _method and the value="delete inside the form. Check the docs for more details.
There is also a note about this in LaravelCollective package documentations
Note: Since HTML forms only support POST and GET, PUT and DELETE methods will be spoofed by automatically adding a _method hidden field to your form.
Finally, if you must use an anchor tag <a>, you could use javascript to listen to the click event and submit a form with DELETE method.
Update to add an example:
You can find an example of using an anchor tag to submit the form, in the default app layout in the framework here
And this is a modified version to submit a delete request:
<a class="dropdown-item" href="#"
onclick="event.preventDefault();
document.getElementById('destroy-form').submit();">
{{ __('DELETE') }}
</a>
<form id="destroy-form" action="{{ route('letters.destroy', $entity) }}" method="POST" style="display: none;">
#method('DELETE')
#csrf
</form>
You cant. If you want to make a DELETE request you need to spoof it via a form (method POST, _method DELETE) or use Javascript.
Hyperlinks will cause new requests which will be GET requests. That is just how the web works.

Session Flash Messages not showing anywhere despite coding

I am following strict practices as per 5.2 docs, yet the validation is driving me insane.
1) This is a code snippet that I have in the controller right after the $property->save(); function
Session::flash('success', 'property table filled');
Session::flash('errors', 'These are the errors');
etc
Second issue is that the Validate method, if there are errors, it reverts back to the Create Form Page, but instead of binding the existing data in the Fields, it wipes everything out, so that I have to start the Form from scratch. Besides, it does not display any error messages
public function store(Request $request){
$this->validate($request, array(
'country' => 'bail|required|max:100',
'region' => 'bail|required|max:100',
etc
According to the documentation, this alone should, in case of fail to pass, revert back to the Create method above (the one that shows the Form to Create the Post) and issue a set of errors.
Since I am using a Resource Controller, all of the Routes are included in one line, and also, all of the Controllers and stuff are inside the Web Middleware:
Route::group(['middleware' =>'web'], function(){
Route::auth();
Route::get('/', function () {
return view('welcome');
});
Route::get('/home', 'HomeController#index');
Route::resource('property', 'PropertyController');
});
This is the flawless snippet I have in the partials which is included in the Layout, so that it will display messages (success or fail) for every page that has a session:
#if(Session::has('success'))
<div class="alert alert-success" role="success">
<strong>Success: </strong> {{Session::get('success')}}
</div>
#endif
<div class="row">
#if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
If instead, the storing of the data in the table database was successful, it still won't echo out any success flash session message.
Question:
Were there any errors in the submitted data, why doesn't display anything and why does it empty me all of the fields. The effect of emptying the fields is the same thing that would happen not when you refresh the Page, (filled fields stay filled) but when you click and press in the URL box of the browser.
Well, this is one of the cases where you get the answer but you dont know exactly why it works.
While I already had the Resource Controller Method inside the Web Middleware in the routes file, it turns out that I did not have to place the (or rather it was not enough) this:
Session::flash('success', 'property table filled');
Session::flash('errors', 'Posting failed');
in the Property Controller (and then this controller inside the Web Middleware in the Routes File), but actually those two lines on their own right in there in th Routes File
Session::flash('success', 'property table filled');
Session::flash('errors', 'Posting failed');
This is so much so that if I remove them from the Controller, this still works, but if I remove them from the Routes File, it doesn't.

Laravel 4 - Update Div Using Ajax

I'm using Laravel 4 and am trying to update a (#articles) div with the new articles that are retrieved from an ajax request. When I inspect the page and view the Network section, I can see the POST requests being fired off and it's not showing any errors (eg, articles appear to be returned). However, unfortunately, the #articles div is not being updated with the new information. Yet, if I do a browser refresh, the new articles are displayed.
Routes.php
Route::any("/dashboard/latest_sa", [
"as" => "dashboard/latest_sa",
"uses" => "DashboardController#latest_sa"
]);
controllers/DashboardController.php
Class DashboardController extends \BaseController
{
...
protected function latest_sa()
{
if( Request::ajax() )
{
// called via ajax
$articles = Articles::orderBy('published_at', 'desc')->paginate(20);
return json_decode($articles);
}
else
{
// fresh page load
$articles = Articles::orderBy('published_at', 'desc')->paginate(20);
return $articles;
}
}
...
}
app/views/dashboard/default.blade.php
...
#section("content")
// defined in /public/js/main.js
<script type="text/javascript">
callServer();
</script>
<div class="col-xs-4 col-sm-4 col-md-4 col-lg-4">
<h4>Latest Articles</h4>
<div class="articles">
<ul>
#foreach ($articles as $article)
<li>
<img src="{{ $article->user_image }}" alt="{{ $article->article_title }}" />
{{ $article->article_title }}
<div class="details">
<span class="author">{{ $article->author_name }}</span>
<span class="created">{{ Helpers::time_ago($article->published_at) }}</span>
<span class="symbol">{{ $article->symbol_title }}</span>
</div>
</li>
#endforeach
</ul>
</div>
{{ $articles->links() }}
</div>
...
/public/js/main.js
function callServer()
{
setInterval(function(){
$.ajax({
type: "POST",
url: "dashboard/latest_sa",
success:function(articles)
{
$(".articles").html(articles);
}
});
},5000);
}
JS is hardly my strong suit, so I'm not sure what I'm doing wrong here.
And, for clarity sake, the reason why I'm trying to update all of the articles in the div is so that the Helpers::time_ago method also gets called, instead of just fetching the new articles. This way, it properly shows how long ago the article was published (eg, less than a minute ago, a minute ago, a hour ago, a day ago, etc) without refreshing the page. Essentially, I'm trying to kill two birds with one stone; update the div with the most recent articles, and update the remaining article's published_at attribute using my Helpers::time_ago method. If there is a more effective / efficient way of doing this, feel free to correct me. This seems rather crude, but since it's only for personal use and will never be used for commercial purposes, it suits my needs (not that that excuses bad code).
Nonetheless, from my fairly basic understanding, the JS should be doing the following steps:
1) Fire a POST request off to the /dashboard/latest_sa route
2) Execute the DashboardController#latest_sa action
3) Return a DB collection of all $articles ordered by the latest published date, and paginated
4) Pass the $articles collection back to the JS success attribute (as articles)
5) Fire the anonymous function, with the articles collection as an argument
6) Update the corresponding inner HTML with the results from the articles collection
The logic sounds right, so I'm pretty sure this is going to be a human error (98% of the time it is, after all. lol). Hopefully, someone here will be able to see the (probably glaring) problem in the logic and point me in the right direction.
In the meantime, I'm going to keep toying around with it.
I look forward to your thoughts, ideas, and suggestions. TIA.
EDIT:
Well, I found one of the problems; the articles div is a class, and in the JS I'm referring to it as an id. I fixed that, and now after the timeInterval, the article's div is "updated" but no results are being displayed (none, zippo, nadda).
Yet, if I directly access the /dashboard/latest_sa URI I get the valid JSON response that I'm expecting. So, albeit I am closer, I am still missing something.
EDIT 2:
Okay, in the controller, I made some changes which can be seen above, where I am now doing a json_decode on the $articles, before returning them to be passed into the view. With that in place, the articles are showing back up again after the timeInterval has elapsed, however, the new articles and the published_at for the existing articles are not being updated. After reviewing Inspect -> Network, it shows that the server is responding with a 500 Internal Server Error from the ajax POST request.
Hrm... Seems like I'm going in circles. Sounds like a good time to take a break and go for a walk. ;)
EDIT 3:
Well, I modified my Helpers class and added in the following method to check if the $article is a json object.
public static function isJson($string)
{
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
app/views/dashboard/index.blade.php
#foreach ($articles as $article)
<?php
if( Helpers::isJson($article) )
{
$article = json_decode($article);
// dd($article) // when uncommented it returns a valid PHP object
}
?>
<!-- Iterate over the article object and output the data as shown above... -->
#endforeach
As you can see, (for the time being) inside of my view's foreach($articles as $article), I run Helpers::isJson($article) as a test and decode the object if it is json. This has enabled me to get passed the 500 Internal Server Error message, populate the articles div with the results on the initial load, and after the ajax POST request is fired off, I'm getting back a server response of 200 OK according to Inspect -> Network. However, after it updates the div, it doesn't show any articles.
Around, and around I go... I think it's time I take that break I keep murmuring about. ;)
Any thoughts, suggestions and / or ideas are greatly welcomed and appreciated.
At first, you should know that, when you return a collection from the controller/route, the response automatically turns in to a json response so, you don't need to use json_decode() and it won't work, instead, you may try something like this (from your controller for ajax):
$articles = Articles::orderBy('published_at', 'desc')->paginate(20);
return View::make('defaultAjax')->with('articles', $articles);
Since building the HTML in the client side using the json data received from server side would be tough for you so, you may return HTML from the server with the generated view instead of json, so you may try something like this in your success handler:
success:function(articles) {
$(".articles").html(articles);
}
Now create a view for ajax response without extending the template like this:
//defaultAjax.blade.php used in the controller for ajax response
<ul>
#foreach ($articles as $article)
<li>
<img src="{{ $article->user_image }}" alt="{{ $article->article_title }}" />
{{ $article->article_title }}
<div class="details">
<span class="author">{{ $article->author_name }}</span>
<span class="created">{{ Helpers::time_ago($article->published_at) }}</span>
<span class="symbol">{{ $article->symbol_title }}</span>
</div>
</li>
#endforeach
</ul>
{{ $articles->links() }}
Notice, there is no #extendds() or #section(), just plain partial view, so it'll be rendered without the template and you can insert the ul inside the .articles div. That's it.
$("#articles").html(articles); ->> $(".articles").html(articles);

Resources