Abstract controller - php

I have a standard controller, whose logic the same in other controllers. These are admin panel controllers.
<?php
namespace App\Http\Controllers;
use App\Http\Requests\PageRequest;
use App\Page;
use App\Repositories\PageRepository;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class PageController extends Controller
{
private const REDIRECT_INDEX = 'PageController#index';
protected $model;
public function __construct()
{
$this->model = new PageRepository(new Page());
}
public function index(): View
{
$pages = $this->model->all();
return view('pages.index', compact('pages'));
}
public function create(): View
{
return view('pages.create');
}
public function store(PageRequest $request): RedirectResponse
{
(new Page($request->all()))->save();
return redirect()->action(self::REDIRECT_INDEX)->with('status', 'Created');
}
public function show(Page $page): View
{
return view('pages.show', compact('page'));
}
public function edit(Page $page): View
{
return view('pages.edit', compact('page'));
}
public function update(PageRequest $request, Page $page)
{
$page->fill($request->all())->save();
return redirect()->action(self::REDIRECT_INDEX)->with('status', 'Updated');
}
public function destroy(Page $page): RedirectResponse
{
$page->delete();
return redirect()->action(self::REDIRECT_INDEX)->with('status', 'Deleted');
}
}
In other controllers different only
const REDIRECT_INDEX
$model
vies - 'pages.index', 'pages.create' and etc.
PageRequest $request - request with validation
Page $page - auto finded row from db by slug
So I have PageController, NewsController, TabController, TypeController with the same logic. How can I abstract?

Related

Why is the data lost in the parent constructor?

I have a class like this:
class BaseApiController extends Controller
{
protected $user;
function __construct()
{
$this->configHeaderRequest();
$this->validateToken();
}
protected function validateToken()
{
$this->middleware(function ($request, $next) {
if (Auth::guard('web')->user()) {
$this->user = Auth::guard('web')->user();
}
var_dump($this->user); //=> This is "user object" as well
return $next($request);
});
}
}
And another class that is extended from the class above:
class NewsController extends BaseApiController
{
public function __construct(Request $request)
{
parent::__construct($request);
$this->middleware(function ($request, $next) {
if (empty($this->user)) {
return $this->sendFailedResponse('Unauthorized Request', 401);
}
return $next($request);
});
}
public function search(Request $request): JsonResponse
{
var_dump($this->user); //=> but there is "null"
}
}
As you can see, $this->user will be null when I needed the user's object inside the search(Request $request) method while surprisingly the user's object exists inside the validateToken() method. Why? And how can I access the user's object there?

How to properly connect models and custom classes in Laravel

I do not understand how to properly connect the model and filter. I implemented this using Dependency Injection in the controller, but I don’t like the fact that it needs to be done in every method where filtering should be applied. It would be very convenient if the model itself understood which class with filters to use.
Tell me how to do better.
Filtration Class:
namespace App\Classes\Filter;
class QueryFilter
{
protected $query;
protected $params;
public function apply($query, $params)
{
$this->query = $query;
$this->params = $params;
foreach ($this->filters() as $filter => $value){
if(method_exists($this, $filter)){
$this->$filter($value);
}
}
return $this->query;
}
public function filters()
{
return $this->params;
}
}
Heirs implement filters for different models:
namespace App\Classes\Filter;
class PositionFilter extends QueryFilter
{
public function title($value)
{
$this->query->where('title', 'LIKE', "%$value%");
}
}
class GasStationFilter extends QueryFilter
{
public function number($value)
{
$this->query->where('number', 'LIKE', "%$value%");
}
public function region($value)
{
$this->query->whereHas('region', function ($query) use ($value){
$query->where('regions.id', $value);
});
}
}
In the controller, I inject the desired class with filters and apply filtering like this (I use scope in the model):
public function index(GasStationIndexRequest $request, GasStationFilter $filters)
{
$gasStations = GasStation::with('region')
->filter($request->validated(), $filters)
->take(10)
->get();
return GasStationSelect2Resource::collection($gasStations);
}
Model:
namespace App\Models;
class GasStation extends ListModel
{
public function region(): BelongsTo
{
return $this->belongsTo(Region::class);
}
public function scopeFilter($query, $params, $filters) : Builder
{
return $filters->apply($query, $params);
}
}

UnitTest a PHP CodeIgniter Controller that calls a model

I'm testing my CodeIgniter project with PHPUnit Testing framework (CITest.php). When the function test_model(), calls the model directly to get the details of an user, it works perfectly. But when I do the same via a controller by calling the function test_controller(), it does not output anything (When I debugged, the model itself doesn't gets called). I even verfied if the post data is passed correctly by creating a function test_post_data(). Am I missing something?
I could only find online resources to test the mdoel directly or a controller separately. But I couldn't find any useful link which calls a controller that triggers the model.
CITest.php
class CITest extends PHPUnit_Framework_TestCase
{
private $CI;
public function setUp()
{
$this->CI = &get_instance();
$this->CI->load->model('Test_model');
$this->model = $this->CI->My_model; // load the model
$this->auth = new Test_controller; // load the controller
}
public test_model() {
$user_id = 6;
print_r($this->model->getUserData($user_id));
}
public test_post_data() {
$_POST['useR_id'] = 22;
print_r($this->model->check_post_data());
}
public test_controller() {
$_POST['useR_id'] = 22;
print_r($this->model->get_user_data());
}
}
Test_controller.php
class Test_controller extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('Test_model');
}
public function check_post_data() {
return $this->input->post();
}
public function get_user_data() {
$user_id = $this->input->post('user_id');
return $this->Test_model->getUserData($user_id);
}
}
Test_model.php
class Test_model extends CI_Model {
public function __construct()
{
parent::__construct();
}
public function getUserData($user_id) {
return $this->db->select("*")
->from("users")
->where("user_id", $user_id)
->get()->result_array();
}
}
The code in CITest.php
public test_controller() {
$_POST['useR_id'] = 22;
print_r($this->model->get_user_data());
}
should be like the following?
public test_controller() {
$_POST['useR_id'] = 22;
print_r($this->auth->get_user_data());
}

how do we set menu list globally

Initially I have to attach with each action :-
Here we first fetch menu detail then pass in to view section.
Class ManageadministratorController extends Controller {
public $data_menu;
public function __construct() {
$this->middleware('auth');
$obj = new General;
$this->data_menu=$obj->displaymenu();
}
public function index()
{
$obj = new General;
$permission = $obj->checkViewPermission("manageadministrator");
$query= Adminlogin::get();
return View::Make('admin.manageadministrator.manage',array('record'=>$query,'menu_list'=>$this->data_menu));
}
function add()
{
return View::Make('admin.manageadministrator.add',array('menu_list'=>$this->data_menu));
}
}
You can register a custom service provider or use AppServiceProvider:
public function boot()
{
$obj = new General;
$data_menu = $obj->displaymenu();
view()->composer('admin.manageadministrator', function($view) {
$view->with('menu_list', $data_menu);
});
}
Or use a dedicated class:
// app/Providers/AppServiceProvider.php
public function boot()
{
view()->composer('admin.manageadministrator', 'App\Http\Composers\MasterComposer');
}
// app/Http/Composers/MasterComposer.php
use Illuminate\Contracts\View\View;
class MasterComposer {
public function compose(View $view)
{
$obj = new General;
$data_menu = $obj->displaymenu();
$view->with('menu_list', $data_menu);
}
}

In laravel view() function within a controller, can this detect an AJAX request

In my controller, I have something like the following:
public function index()
{
$questions = Question::all();
return view('questions.index', compact('questions'));
}
However, I would like this route to also be used by my ajax requests. In which case, I'd like to return JSON. I'm considering the following:
public function index()
{
$questions = Question::all();
return $this->render('questions.index', compact('questions'));
}
public function render($params)
{
if ($this->isAjax()) {
return Response::json($params);
} else {
return view('questions.index')->with($params);
}
}
..by the way, I haven't tested any of this yet, but hopefully you get the idea.
However, I was wondering if I can alter the built in view(...) functionality itself to keep things even lighter. So I just keep the following:
public function index()
{
$questions = Question::all();
// this function will detect the request and deal with it
// e.g. if header X-Requested-With=XMLHttpRequest/ isAjax()
return view('questions.index', compact('questions'));
}
Is this possible?
You probably want to make custom response:
add ResponseServiceProvider.php
namespace App\Providers;
use Request;
use Response;
use View;
use Illuminate\Support\ServiceProvider;
class ResponseServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
Response::macro('smart', function($view, $data) {
if (Request::ajax()) {
return Response::json($data);
} else {
return View::make($view, $data);
}
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
Add 'App\Providers\ResponseServiceProvider' to providers list in config/app.php:
'providers' => [
'App\Providers\ResponseMacroServiceProvider',
];
Use new helper in controller:
return Response::smart('questions.index', $data);
Simply check if the Request is an Ajax request in your index method itself.
public method index() {
$questions = Question::all();
if(\Request::ajax())
return \Response::json($questions);
else
return view('questions.index', compact('questions'));
}
Use Request::ajax(), or inject the request object:
use Illuminate\Http\Request;
class Controller {
public function index(Request $request)
{
$data = ['questions' => Question::all()];
if ($request->ajax()) {
return response()->json($data);
} else {
return view('questions.index')->with($data);
}
}
}
Your view should never know anything about the HTTP request/response.
I guess the simple method is just to put a method inside the parent Controller class:
use Illuminate\Routing\Controller as BaseController;
abstract class Controller extends BaseController {
...
protected function render($view, $data)
{
if (Request::ajax()) {
return Response::json($data);
} else {
return view($view, $data);
}
}
}
and then instead of doing view('questions.index, $data);, do $this->render('questions.index', $data);

Categories