Backpack for Laravel: how to add 'delete' per-line button conditionally? - laravel

I've a parent model which can have n childs
So I want to show delete button ONLY if it has not children (hasMany relationship must return 0 records).
How can I show 'delete' link in each lines of a table (in the list operation), but ONLY if a condition is valid?

The easiest option I see is to use a custom view for the Delete button, instead of the one in the package. Depending on how wide you want this change to be made:
A. If you want to do that across all CRUDs - it's easy, just publish the view by running:
php artisan backpack:publish crud/buttons/delete
This will place the file in your resources/views/vendor/backpack/crud/buttons, for you to change however you like. Inside the delete button view you have the current entry available as $entry so you can do something like $entry->children()->count() if you want. Be mindful that this will be run ONCE PER LINE so if you show 50 lines in the table for example, you'd need to find a way to optimize this.
B. If you want to do that for just one CRUD (eg. do it for Categories but not for Products), then you can do the same thing (publish the button view), but rename the button to something different like delete_if_no_children.blade.php so that it doesn't get used automatically for all CRUDs. Then use it only inside the controllers you want, inside setupListOperation(), by removing the "stock" delete button and adding yours:
// using the Backpack array syntax
$this->crud->removeButton('delete');
$this->crud->addButton('line', 'delete', 'view', 'crud::buttons.delete_if_no_children', 'end');
// using the Backpack fluent syntax
CRUD::button('delete')->view('crud::buttons.delete_if_no_children');

Use
withCount('children')
Docs: withCount
Additionally you can wrap delete buttons with blade directive
#can('destroy', $model) disabled #endcan
Docs: #can
<?php
namespace App\Policies;
use App\Models\Model;
use App\Models\User;
class ModelPolicy
{
public function update(User $user, Model $model)
{
return $model->children_count === 0;
}
}
Docs: policy

A hasMany relationship will return an empty Collection when there are no associated records. You can then proceed to call isEmpty() on the collection to verify there are no child records (children).
Example in PHP:
$parents = Parent::with('children')->get();
And then in your template you can do:
#foreach($parents as $parent)
#if($parent->children->isEmpty())
<button>Delete</button>
#endif
#endforeach

Related

Laravel - updateOrCreate or a sync method?

I'm trying to see what my best route should be but for the sake of ease I will make this sound much simpler than it is.
I have two models, one called donor and one called donation. For the relationships, the donor has many donations.
In my edit form of the donor model, I have the ability to create, update or delete the donations from the same form.
Updating and creating are easy at the moment because I can use updateOrCreate. But what happens if I want to delete a donation?
Would I perform an actual query filtering out the ids of the donations that were still on the edit form (and therefore not deleted by the user) and then delete the models from there? Or is there a better way of handling this action?
Thanks in advance.
In your DonationController you could select the donations you want to delete with a query like this:
public function destroy($id)
{
$donation = Donation::where('donor_id','=', $id)->get();
$donation->delete();
}
In your view you could make a delete form and them send him to this function in the controller. I think the best way to do it is make a foreach and loop the donations from the donor inside.
#foreach($donor->donations as $donation)
{
<form method="POST" action="{{route('donor.destroy', $donation->id)}}">
<button type="submit">Delete {{$donation->id}}</button>
</form>
}
#endforeach

Laravel 5 Bind data to var for menu in header.blade.php

What is the best solution for creating a menu.
I have $menudata (dynamic from dbase) and want to pass it to a heade.blade.php to generate top menu.
$menudata = Menu::all();
#foreach($menudata as $value).......
How can i achieve that? What's the best way to do it?
Add below code within the boot method of the AppServiceProvider
View::composer('*', function($view)
{
$view->with('menudata', Menu::all());
});
Here, * means all views will receive $menudata.
Now you can use this like
#foreach ($menudata as $menu).....
If the menu is visible in every page then add the following in any service provider (e.g. in the AppServiceProvider):
public function boot() {
// ...
View::share("menudata",Menu::all());
//...
}
Then you can use the menu data in any view e.g.
#foreach($menudata as $value).......
Laravel-menu provides three rendering methods out of the box. However you can create your own rendering method using the right methods and attributes.
As noted earlier, laravel-menu provides three rendering formats out of the box, asUl(), asOl() and asDiv(). You can read about the details here.
{!! $MyNavBar->asUl() !!}
You can also access the menu object via the menu collection:
{!! Menu::get('MyNavBar')->asUl() !!}

Customize #can() to show a permissions overlay for pages/sections

At work I maintain a fairly complex Laravel application which is still growing as new features are implemented and improved upon.
We have non-technical administrators in this system who manage other users permissions and sometimes it can be hard to know what permission ends up blocking a user from accessing a certain page or what might give a user too much access. Better descriptions for permissions and the ability to simulate a user to see what they have access to is already something we have done.
In addition to this we would like to toggle overlays for permissions defined in blade templates, we might defines this permissions with
#can('update', $post)
<!-- Menu button to update a $post -->
#endcan
or
#can('manage_user_roles_and_permissions')
<!-- A table with many different functions
for managing user roles + permissions -->
#endcan
Is there a way I can modify the way the #can() works in blade templates so that I can add some javascript to show a popover for where a section starts and ends, like "The permission 'Show Post' is needed for this menu button to show" or "To see the following section a user needs the 'Manage user roles and permissions' permissions". Or even better if I could add a div with a red border around the section.
How can I append additional javascript/html where #can() is used in a blade template to show an overlay.
To solve this issue I need to extend blade, see Extending Blade in the Laravel documentation.
The following is a quick test that I did just to see if this was possible. $value in this case is a string which contains the content of a blade file before being processed. So I can use preg_match_all() to find the #can statements and then append my javascript where needed. I can find the #endcan in the same way but it is harder to know which #endcan belongs to which #can but it should be fairly easy to match from this point on.
<?php
namespace App\Providers;
use Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Blade::extend(function($value)
{
$can_array = array();
preg_match_all('/(\s*)#(can)\(([^\)]*)\)(\s*)/', $value, $matches);
if (count($matches) > 0 && isset($matches[3])) {
foreach ($matches[3] as $match) {
if (!in_array($match, $can_array)) {
$can_array[] = $match;
}
}
}
foreach ($can_array as $ca) {
$value = str_replace("#can(" . $ca . ")", "#can(" . $ca . ") \r\n <!-- My javascript code goes here! -->", $value);
}
// TODO need to figure out a better way to handle this
$value = str_replace("#endcan", "#endcan \r\n <!-- Section ended here -->", $value);
return $value;
});
}
...
My source code now looks like this when viewing it, goal achieved!

Joomla MVC Module delete model

I'm a newbie on Joomla developing and I'm trying to fix an old administration module made by 'someone before me'. Module's been developed using MVC Components, it has several CRUDs and I'm stucked at deleting an item. The template view adds the toolbar icon like this:
JToolbarHelper::deleteList('', 'paises.delete', JTOOLBAR_DELETE);
It also has at the list controller (DistribuidoresControllerPaises), the getModel function:
public function getModel($name = 'Pais', $prefix = 'DistribuidoresModel', $config = array('ignore_request' => true))
{
$model = parent::getModel($name, $prefix, $config);
return $model;
}
The model class:
class DistribuidoresModelPais extends JModelAdmin
When selecting an item on the list, and clicking the trash button an empty page opens with this ending:
administrator/index.php?option=com_distribuidores&view=pais
If I come back to grid, the item still remains.
Any suggestion?
Thanks in advance
You can debug this by enabling debugging from Joomla configuration or you can try to to check with exit with in "delete" function of "paises" controller and can check you get item ids in post request or not.
Also you are using view "pais" also using model "pais" then why you are using "paises" controller for delete function, you should use "pais" controller to delete.
Also provide delete function which you are using to delete items, it may contain some issue.

How to send data from different controllers/actions to the same view?

This is my first experience with Laravel, and so far I'm having some difficulties passing data to views. My app is a single page website with one menu on the top listing all the product categories and below there is a grid of thumbnails for each item or product. Visitors are able to filter the products by their category of choice.
Route::get('home/{category}', array('as'=>'itemshome', 'uses'=>'ItemsController#index'));
So in my ItemsControllers I fetch some items from the item model and pass them to the view.
class ItemsController extends \BaseController {
public function index($category)
{
return View::make('home/index', ['items' => Item::where('publishtime', '<',
date('Y-m-d H:i:s'))->where('category_id','=',$category)->paginate(24)]);
}
At this point I'm not sure if I should send data from the Category model to the home view using the ItemsController, or if it would it be a better approach to define a new CategoryController and pass the values from there.
You can't just use another controller to send data to the same view during the same request.
Either add it to the view make call:
return View::make('home/index', [
'items' => Item::where('publishtime', '<', date('Y-m-d H:i:s'))->where('category_id','=',$category)->paginate(24),
'categories' => Category::all()
];
Or, if the category data actually has nothing to do with the items controller but is needed by the view, you could register a view composer
View::composer('home/index', function($view){
$view->with('categories', Category::all());
});
Now every time when the home/index view gets rendered, categories will be injected.
You can actually place the view composer anywhere you want. However I recommend you add a new file app/composers.php to store all your view composers. Then you need to include it somewhere. For example in app/start/global.php at the bottom:
require app_path().'/composers.php';

Resources