Background Image

Customizing & Testing

Up to 1.0, customizing LaraAdmin meant forking the dwij/laraadmin package. Since 1.1 there is nothing to fork: LaraAdmin's code is part of your application. Change controllers, models, views, the theme and even LAModule directly, and commit them like the rest of your code. To get new LaraAdmin versions later, merge them with git (see Upgrading). The fewer LaraAdmin core files you change, the easier those merges are.

After generating a CRUD, its files are ordinary Laravel code:

  • app/Http/Controllers/LA/CoursesController.php: add actions, change what the listing shows (dtajax()), or redirect elsewhere after saving. Keep the LAModule::hasAccess() checks.
  • app/Models/Course.php: add relations, scopes and casts.
  • app/Observers/CourseObserver.php: react to records being created, updated or deleted.
  • resources/views/la/courses/*.blade.php: change the layout. Use @la_input($module, 'fees') and @la_display($module, 'fees') to place single fields, so field types, labels and access stay handled for you.

The generator builds every new module from app/Stubs/: controller.stub, model.stub, observer.stub, views/*.blade.stub, routes.stub, migration.stub, language.stub. Edit them to change what every new module looks like, for example to add a column to all listings or a default scope to all models. Keep the __token__ placeholders.

When you change a pattern in your existing modules (say, a new access check), change the matching stub too, or the next generated module brings the old pattern back.

Don't remove the markers the generator inserts at (see Architecture).

Add your own admin routes inside the second group of routes/admin.php (above its last });, which the generator also uses), and check access in the controller:

// routes/admin.php, inside the 'permission:ADMIN_PANEL' group
Route::get(config('laraadmin.adminRoute').'/reports/courses', 'ReportsController@courses');

// app/Http/Controllers/LA/ReportsController.php
public function courses()
{
    if (! LAModule::hasAccess('Courses', 'view')) {
        return redirect(config('laraadmin.adminRoute').'/');
    }
    return view('la.reports.courses', ['courses' => Course::all()]);
}

Make the view @extends('la.layouts.app') to get the admin layout, and add a custom link in the Menu Manager. Use POST for anything that changes data.

The admin theme is AdminLTE 2 on Bootstrap 3, in resources/less/. After changing it, rebuild public/la-assets/ with npm install and npm run dev (Laravel Mix 6). Skin and layout can be switched without code on the Configure page.

The test suite

LaraAdmin 1.13.1 comes with PHPUnit 12 tests. They need a MySQL database named laraadmin_test (see phpunit.xml); each test migrates and seeds it from scratch. Run them with ./vendor/bin/phpunit.

TestCovers
Feature/AdminSmokeTestEvery admin page and listing data endpoint
Feature/ModuleSchemaTestAll 31 field types: create, save again, change type
Feature/CrudGeneratorTestRuns la:crud, uses the generated CRUD, then deletes the module. It writes real files and restores them afterwards.
Feature/Access/*Every route as six users: guest, customer, admin (admin panel, no module access), viewer (read-only role), manager (full module access) and super_admin. Forms are filled from the real rendered HTML.
Feature/Access/RouteCoverageTestFails when a route is not listed in RouteCatalogue

Tests that run the code generators snapshot the files they touch and restore them in tearDown(). If a run crashes with a PHP fatal error, tearDown() doesn't run: check git status afterwards. The backup tests need mysqldump.

Testing your modules

When you generate a module, RouteCoverageTest starts failing: that is intended. Add the module's routes to tests/Feature/Access/RouteCatalogue.php:

// PAGES: GET pages opened by every role. uri => [group, module, access, status]
'admin/courses' => ['panel', 'Courses', 'view', 200],
'admin/courses/{course}' => ['panel', 'Courses', 'view', 200],
'admin/courses/{course}/edit' => ['panel', 'Courses', 'edit', 200],
'admin/course_dt_ajax' => ['panel', 'Courses', 'view', 200],

// ACTIONS: every other route => the test that covers it
'POST admin/courses' => CourseActionsTest::class.'::test_add_course',

Use the traits in tests/Concerns: ActsAsRoles creates the six users and logs in as one ($this->asRole('viewer')), and FillsForms reads a form from the rendered page and submits it, so tests use the same fields as a real user.

GET routes with side effects

Never let a crawler or link checker follow every admin link. Some GET routes change things: la_modules/generate_crud|generate_migr|generate_update|generate_migr_crud/{id} regenerate source files and migrations, and la_module_fields/{id}/delete, la_configs/ajax_destroy/{id}, la_modules/{id}/set_view_col/{col}, update_local_upload_paths and git-status change data or run commands.

Found a bug or improved something that other LaraAdmin users would want? Fork the repository on GitHub, make the change with a test, run ./vendor/bin/phpunit and composer sniff, and open a pull request. Report security issues privately to ganesh@dwij.in. Known issues and planned work are listed in engg-debt.md.


What's Next ?