laravel destroy just deletes last added stuff - php

I am a Laravel newbie. While writing and testing my code, I noticed that my destroy method isn't working right anymore and I cannot find the mistake I've made. So I hope you can help me out.
Whats my (software) target? I want to manage "projects". Every project has many reports. So I got a page with all created projects and I got a page with all reports listed in a table with buttons for "modify" and "delete". I finished all the CRUD stuff for projects and reports, when I recognized that, if I am hover over the delete-button of a report or project, the right ID of the chosen report or project is shown. If I am hitting the delete-button a dialog plopps up and a message is shown: "Do you really want to delete..." with "yes" and "no" buttons. So if I am pressing the "yes"-button Laravel is going to delete the last added database entry.
Even the projects as the reports too got their own controller. But both are using the same _messages.php. I think, that my mistake is in that file.
Excerpt from _messages.php:
#if(Session::has('sweet_alert.confirmDeleteReport'))
<div class="alert alert-warning" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<div class="row">
<div class="col-xs-12">
<strong>Achtung!</strong> {{Session::get('sweet_alert.confirmDeleteReport')}}
</div>
</div>
<div class="row">
<div class="col-md-3 col-md-offset-8">
<a class="btn btn-danger" href="{{ route('reports.destroy', $report->id) }}" title="Löschen" data-token="{{csrf_token()}}" data-method="delete">Löschen</a>
<a class="btn btn-default" data-dismiss="alert">Abbrechen</a>
</div>
</div>
</div>
#endif
Excerpt from reportcontroller.php:
public function destroy(Report $report)
{
$report->delete();
Session::flash('sweet_alert.success','Der Bericht vom ' . $report->date . ' mit der Berichtsnummer ' . $report->reportNumber . ' wurde erfolgreich gelöscht.');
return redirect()->route('reports.index');
}
public function delete(Report $report) {
Session::flash('sweet_alert.confirmDeleteReport', 'Soll der Bericht vom ' . $report->date . ' mit der Berichtsnummer ' . $report->reportNumber . ' wirklich gelöscht werden? Dieser Vorgang kann nicht rückgängig gemacht werden.');
return redirect()->route('reports.index');
}
Might there be the fault within the session? I flushed the cache by executing php artisan config:cache but with no luck. Every idea is welcome.
Greetings

If I understand correctly you have a page which lists all reports. Each report has a delete button, and that button route maps to the delete() method you include above. That method flashes a msg to the session, and reloads your report index.
Now on your report index, all reports are listed again, probably inside something like #foreach ($reports as $report). At the bottom of the page, you have the extract you have shown. This uses $report->id, which on this page, after looping through all $reports, is just the last report in that collection. So all it will do is delete the last report.
There are a few solutions I can see:
1) Flash the id of the report you want to delete along with your message in the delete() method. I am not familiar with sweet_alert but can you flash an array, your msg and the id? Something like:
Session::flash('sweet_alert.confirmDeleteReport', [
'id' => $report->id,
'message' => 'Soll der Bericht ...'
]);
And then of course use that id in your alert:
<a class="btn btn-danger" href="{{ route('reports.destroy', Session::get('sweet_alert.confirmDeleteReport.id')) }}" ... </a>
Maybe the syntax is wrong, but I am assuming flashing an array is possible.
2) The way I usually do this kind of thing is use Javascript to populate the alert/modal/whatever with the required data when you click the delete button in the table. Eg your delete buttons (in the main table/list) could have a data-id attribute:
Delete
And some Javascript (pseudo code, implementation depends on how you have things set up):
$('a.delete').on('click', function(e) {
// Prevent the default behaviour of clicking a link
e.preventDefault();
// Find the id of the report clicked
var id = $(this).data('id');
// Update the href of the alert button so it will delete *that* report
$('div.alert a.danger').attr('href', 'reports/' + id);
// ... code to display the alert/modal
});
This way you avoid the page reload to get to your alert/modal. Note this makes your delete() method obsolete, it would not be used any more.
UPDATE
If you need to pass some variable from your view/controller to your external JS file, you can do something like this:
In the view
<script>
var url = "{{ route('reports.destroy', Session::get('sweet_alert.confirmDeleteReport.id')) }}";
</script>
In your external JS (must be loaded after the above inline script)
$('div.alert a.danger').attr('href', url);

Related

How to call my OOP based classes and methods in PHP when buttons clicked or page loads in purely OOP style

Point 1:
So I'm a beginner in PHP OOP and have been asked to complete a project for hiring at a company. They have asked in the guidelines to code in PHP OOP only, and have recently returned my project with one of the following point:
"Procedural PHP code is allowed exclusively to initialize your PHP classes. Rest logic should be placed within class methods."
Now, I am stuck since I have no clue how to call my php code without instantiating my classes in scenarios like such:
Perform a save/delete function when a button is clicked
Show all products from my phpMyadmin database on the homepage automatically
when the page loads
My frontend is made using basic HTML+ JQuery and I'm required to use php oop for all logical handling.
This is a snippet of my code where when a button is clicked I have used isset to call a function or instantiate a class and added calls to functions in the constructor, I dont know how else to call my functions, like I have to instantiate the objects somewhere and call function on the basis of some check when specific event happens.
//placed these checks in a procuctinfo.php class which is called when button save/delete(with action="productinfo.php" method = "post") is clicked
if (isset($_POST['save'])) {
$product = new $_POST['productType'](); //goes to constructor of specific product where method is called to save data
}
if (isset($_POST['delete'])) {
Product::delete(); // static method called since delete is not object specific
}
//here is my constructor of one of the product classes
public function __construct(){ //contructor for one of the products
parent::__construct();
if (isset($_POST['save'])) {
$this->weight =$_POST['weight'];
**$this->setData()**; //method called within constructor which saves data in my db
}
}
Similarly for rendering all products on homepage I have embedded php code within my html in the following way,
<div class="text-center py-2 px-5 justify-content-center d-flex flex-wrap gap-4">
<?php
$DVDs = new DVD([]);
$result = $DVDs->getAllData();
if($result){
foreach($result as $product){
$productObj = new $product['productType']([]);
$productResult = $productObj->getDataProductWise($product);
?>
<div class="col-lg-3 col-md-3 ">
<div class="card">
<div class="bg-image hover-zoom ripple ripple-surface ripple-surface-light"
data-mdb-ripple-color="light">
<div class="form-check delete-checkbox">
<input class="form-check-input m-3 border border-dark rounded-0" type="checkbox" name = "deleteChecked[]" value="<?= $productResult['sku'] ?>" id="flexCheckDefault">
</div>
</div>
<div class="card-body">
<h5 class="card-title mb-3"><?= $productResult['sku'] ?></h5>
<h6><?= $productResult['name'] ?></h6>
<h6 class="mb-3"><?= $productResult['price'] ?> $</h6>
<h6 class="mb-3"><?= $productResult['attribute']?></h6>
</div>
</div>
</div>
<?php
}
}
?>
</div>
Please guide me if there is some purely OOP based way/approach to do this.
Point 2:
I provided them a website link which I created using https://www.000webhost.com/ website but they have asked me to :
"Please provide FE link."
What is this and how can I create it?
I do not like to see $_POST[] inside a constructor. That's purely my opinion, but I can give some reasons: It's raw user input, think about security, and classes should be created to be flexible, not rigid in that the input has to come from the user.
Your code snippets are very short. I cannot do much with those. Only the output code is long enough.
In it I see that you don't actually use objects, you convert them to arrays. Why? getAllData() and getDataProductWise() should not be used in this code. It should simply be getProducts() which should return an array with zero or more product objects.
This means that when you need to display something from an object you do:
echo '<h6 class="mb-3">' . $product->getPrice() . '</h6>';
You can create a method in the DVD class called outputProductHtml($product) to output a product and then you can also do:
public function outputProductsHtml()
{
foreach ($this->getProducts() as $product) {
outputProductHtml($product);
}
}
Keep methods short and simple like this. Now your DVD class can generate HTML for a single product or for all products it contains.

laravel pagination returns different page

I am working on a laravel project, where I get data from an API then I want to display it on pages. I want the return to be spread out across 4 pages, each page with 10 results each. What I have so far, seems like it should work, but I am missing one piece, so any advice and help would be appreciated. So this is how it is suppose to work with code:
1) The users types in a book title in a search box.
<form method=POST action='/search'>
#csrf
<input type="text" name="search_term"/>
<input type="submit" value="Search"/>
</form>
2) there input is then sent to my controller, which queries the google books api.
class search extends Controller {
public function search(){
$current_page = LengthAwarePaginator::resolveCurrentPage();
echo $current_page;
$term = request('search_term');
$term = str_replace(' ', '_', $term);
$client = new \Google_Client();
$service = new \Google_Service_Books($client);
$params = array('maxResults'=>40);
$results = $service->volumes->listVolumes($term,$params);
$book_collection = collect($results);
$current_book_page = $book_collection->slice(($current_page-1)*10,10)->all();
$books_to_show = new LengthAwarePaginator($current_book_page,count($book_collection),10,$current_page);
return view('library.search')->with(compact('books_to_show'));
}
}
3) the results are then displayed on my blade
#extends('home')
#section('content')
#foreach($books_to_show as $entries)
<div class="row">
<div class="col-sm-auto">
<img class="w-50 img-thumbnail" src={{$entries['volumeInfo']['imageLinks']['smallThumbnail']}}/>
</div>
<div class="col-sm">
{{$entries['volumeInfo']['title']}}<br/>
#if($entries['volumeInfo']['authors']!=null)
by:
#foreach($entries['volumeInfo']['authors'] as $authors)
{{$authors}}
#endforeach
#endif
</div>
</div>
#endforeach
{{$books_to_show->links()}}
#endsection
This all works fine and as expected. I get 10 results on the view, and then I have a bar at the bottom which give shows me 4 different pages to choose from.
When I first type in a search term such as "William Shakespeare" My page url is:
localhost:8000/search
But, when I click on any of the pages my url becomes:
http://localhost:8000/?page=2
I understand that the ?page=* is how the pagination determines which page you are viewing, and that should be sent back to the controller. But, I am missing something on sending it back to the controller I think.
Still kind of fresh to this, so any advice is more then greatly appreciated.
LengthAwarePaginator accepts a 5th parameter in its constructor: an array of options.
the path option
$books_to_show = new LengthAwarePaginator($current_book_page, count($book_collection), 10, $current_page, [
// This will fix the path of the pagination links
'path' => LengthAwarePaginator::resolveCurrentPath()
]);
By the way, on a totally different matter, Laravel makes your life easier by slicing the collection for you, check it out:
$current_book_page = $book_collection->forPage($current_page, 10);
Hope it helps :)

How to populate view with database information after AJAX request

Hi there once again SO community. I've been developing a site and so far it's going pretty well. But today after a long day searching for a solution I can't understand nor find what the right path is...
I want to click on a button and a profile page where you can edit the fields appear. I can redirect to the page I want but I don't know how to send the user data so I can populate the fields.
Here is my button code on my view
<button class="btn btn-xs btn-warning dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false" style="border-color: black" id="dados_{{ $user->username }}"> Alterar Dados Pessoais
<i class="glyphicon glyphicon-cog"></i>
</button>
Here is the button AJAX request handler
if((this.id).indexOf("dados") != -1){
var content = this.id.replace("dados_", "");
$.get('callPermissions', {usernameSend:content, '_token': $('meta[name=csrf-token]').attr('content'),}, function(data){
window.location.replace('settings');
});
And here is my callPermission Controller
public function callPermissions(Request $request)
{
if($request->ajax()){
$usernames = Input::get('usernameSend');
if(isset($usernames)){
$user = User::Where('username', '=', $usernames)->first();
$returnHTML = view('userOptions.settings')->render();
return view('userOptions.settings');
}else{
Log::warning("Username não existe na base de dados.");
}
}
}
and here my Settings Controller
public function settings(Request $request)
{
return view('userOptions.settings');
}
And here is the route
Route::get('/callPermissions', 'SidebarController#callPermissions');
I know the controller is wrong and from what I've read I should verify if the AJAX is successful and if it is handle i on the AJAX request. But from what I've understand I'm not using the Controller at all (even though it goes there). How should I send the user information (in this case the username, then I can get everything from the database) and then send it to the view? I've been searching and trying out stuff that doesn't work...since the return view("your_view") on the Controller doesn't work.
Sorry if I've been confusing and if you need additional information feel free to ask!
Thanks for your help!!
Edit: If I return this on the controller
return view('userOptions.settings', compact('user'));
and do a replace with the Ajax request as show above and add this to the settings view
<p> {{ $user->name }} </p>
I get the following error Undefined variable: user (View: C:\wamp64\www\siteXL\ideiasxl\resources\views\userOptions\settings.blade.php)
Is there anyway to send the parameters with a compact alike or I need to send it through the link? Was avoiding to show the username on the url.
Edit2: For further clarification, this works as intended
<button onclick="window.location='{{url('/settings/' . $user->username)}}'" type="button" id="dadosPessoais" class="btn btn-default">Alterar Dados Pessoais
<i class="glyphicon glyphicon-wrench"></i>
</button>
but I was trying not to send id's and usernames through the URL.
If this is not achievable it's ok, but if there's a way I can't find it, that's why I'm asking
I think you have to add a parameter in the Route and receive the data in the controller function. I'd do something like this:
Route:
Route::get('/callPermissions/{user}', 'SidebarController#callPermissions');
Controller:
public function callPermissions(Request $request, $user)
{
//get data related to $user
}
Ajax call:
$.get('callPermissions/'+userIdVariable, {usernameSend:content, '_token': $('meta[name=csrf-token]').attr('content'),}, function(data){
window.location.replace('settings');
});
This would send the user id through the route.
To get the user id with JavaScript, you can make a hidden field in the Blade file and set the user id as the value. For example, if you using Form helper:
{{ Form::hidden('user_id', $user->id, array('id' => 'js-user-id')) }}
And then, in the JavaScript, you can get the value using something like this:
var userIdVariable = $('#js-user-id')->val();

Can't query JSON (Laravel + VueJS)

my problem exactly smiliar with this one cant't query json data in laravel 5.2
Already try to implement the right answer from it but still, no luck.
I don't know why....
Previous, i found this Laravel 5.2 Codeception functional test issue with PUT / PATCH requests too, already try to use suggestion from him, but no luck too.
Here's my Laravel Controller
public function update(Request $request, $id)
{
$phonebook = Phonebook::findOrFail($id);
$phonebook->update($request->all());
// even i try this
// Phonebook::findOrFail($id)->update($request->all());
// return Response::json() or return response()->json();
// No luck
}
My function in vue script for update data
editContact: function(id)
{
this.edit = true
var contactid = this.newContact.ID
this.$http.patch('/api/contact/' + contactid, this.newContact, function (data) {
console.log(data)
})
},
Change my vue script to be like the right answer from question above, same result. No effect.
And my button to do edit like this
<form action="#" #submit.prevent="addNewContact">
<div class="form-group">
<label for="contactName">Name : </label>
<input type="text" v-model="newContact.CONTACTNAME" class="form-control" id="contactName">
</div>
<div class="form-group">
<label for="phoneNumber">Phone number : </label>
<input type="text" v-model="newContact.PHONENUMBER" class="form-control" id="phoneNumber">
</div>
<div class="form-group">
<button class="btn btn-primary btn-sm" type="submit" v-if="!edit">Add new Contact</button>
<button class="btn btn-primary btn-sm" type="submit" v-if="edit" #click="editContact(newContact.ID)">Edit Contact</button>
</div>
</form>
Note :
My route file using resource or manual route always same
Route::resource('/api/contact/', 'PhonebookController');
or
patch('/api/contact/{id}', ['uses' => 'PhoneboookController#update']);
And then, there something strange.
(Maybe i am wrong) there no issue or error if we look the detail. But, if we change to response tab the result was empty
After all that process, nothing happen with the data.
CONTACTNAME should be "Mizukiaaaaaaaa" like first screenshot instead of "Mizuki"
Am I missing something??
Any advise?
Thanks
As I suggested to you, try to invert the params in your update method in your controller.
And to get a response, you have to send it back (with code 200, 400, 401, whatever you want).
public function update($id, Request $request)
{
$phonebook = Phonebook::findOrFail($id);
$phonebook->update($request->all());
// your treatment
return Response::json([
'param' => 'value'
], 200);
}
If you want to debug and see it in you response, you can make a dd('debug')in your method, you'll see it in the Ajax request response.
That should work for you !
After browsing and ask so much people about this, finally found it! There's nothing wrong with the request or response. My mistakes are mutator update that i used and my model.
Updated answer
Reason answered here and then I just changed update function on controller. Here the result
public function update(Phonebook $phonebook, Request $request, $id)
{
// You can add any fields that you won't updated, usually primary key
$input = $request->except(['ID']);
// Update query
$saveToDatabase = $phonebook->where('ID', '=', $id)->update($input);
return $saveToDatabase;
}
My previous answer updated all fields including the primary key, somehow it successful update data, but it leave error for sure (duplicate primary key). The query looks like UPDATE SET field = 'value' without condition.
This case is for model that doesn't have any relation with other models (tables), or the model act as master.

Get correct index with Splice()

I'm attempting to delete items from my AngularJS view. From the view, I'm passing the index & the ID. In the JS controller, I splice(index) from the AngularJS array/model. And I pass the ID to a $http.get to call a delete to my database. This all works.
....
Until I make a request to refresh my page. Every 5 seconds I make a request to update the Angular model and I concat any new data to it. But what I'm finding is it screws up my index values. I assumed it was reordering my index: and reading this StackOverflow thread helped confirm that.
What would happen is that I push delete for the 3rd item on the page, and it deletes that ID from the DB. But it splices the wrong item from the page until I refresh the page.
How do I make sure that I'm always passing the correct index?
View
<li ng-repeat="new in news | filter:query | orderBy:orderProp">
<div class="newsitem" id="newspost{{new.id}}">
<h4 ng-model="news.title">{{new.title}} </h4>
<p ng-model="news.text">{{new.text}}</p>
<a class="btn btn-info" ng-click="visible = true">View article</a>
<a ng-click="deleteNews(index,new.id)" class="btn btn-danger btn-mini">Delete</a>
</div>
</li>
controllers.js
$scope.deleteNews = function(index, id) {
$http.get('/ci/index.php/news/delete_news_ajax/'+id).success(function(){
$scope.news.splice(index, 1);
});
};
Instead of passing the index and id, pass new:
<a ng-click="deleteNews(new)" ...>
Then use indexOf in your controller function:
$scope.deleteNews = function(newsItem) {
$http.get('/ci/index.php/news/delete_news_ajax/' + newsItem.id)).success(function() {
$scope.news.splice($scope.news.indexOf(newsItem), 1);
});
}

Categories