How to extend controller in Codeigniter 4? - codeigniter

How can I create my own core controller in codeigniter 4 like codeigniter 3?

When you say "core" controller I understand you to mean a "base" controller which in CI v3 is often named MY_Controller. If that is what you are asking for it's actually much easier in v4 because of namespaces and the autoloader. Also, there is no need for the trickery of using a prefix like MY_.
Here's just how easy it is. The "base" controller...
File: /application/Controllers/Base.php
<?php namespace App\Controllers;
class Base extends \CodeIgniter\Controller
{
//your code here
}
Then extend the above to create any other controller
File: /application/Controllers/Home.php
<?php namespace App\Controllers;
class Home extends \App\Controllers\Base
{
// Your code here
}
The Home controller will inherit all the properties and methods you define in Base.

You do not need to create another core controller, there is already a core controller provided, which is BaseController.
You need to just extend it to your choice

Try this
<?php namespace App\Core;
use CodeIgniter\Controller;
class Home extends Controller
{
}

see my codes
<?php
namespace Modules\Shared\Controllers;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components
* and performing functions that are needed by all your controllers.
* Extend this class in any new controllers:
* class Home extends BaseController
*
* For security be sure to declare any new methods as protected or private.
*
* #package CodeIgniter
*/
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\RESTful\ResourceController;
use Modules\Auth\Config\Services;
use Myth\Auth\AuthTrait;
use Psr\Log\LoggerInterface;
use Modules\Shared\Interfaces\UrlQueryParamInterface;
use Modules\Shared\Libraries\UrlQueryParam;
class ApiController extends ResourceController
{
use AuthTrait;
protected $format = "";
public object $userObject;
public UrlQueryParamInterface $urlQueryParam;
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* #var array
*/
protected $helpers = [
'cookie',
'url',
'from',
'filesystem',
'text',
'shared'
];
/**
* Constructor.
*
* #param RequestInterface $request
* #param ResponseInterface $response
* #param LoggerInterface $logger
*/
/**
* #var string
* Holds the session instance
*/
protected $session;
public function __construct()
{
$this->userObject = (object)[];
}
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
$this->urlQueryParam = new UrlQueryParam($request);
$requestWithUser = Services::requestWithUser();
$this->userObject = $requestWithUser->getUser();
}
}
<?php
namespace Modules\Common\Controllers;
use Modules\Common\Config\Services;
use Modules\Common\Entities\AdvertisementEntity;
use CodeIgniter\HTTP\ResponseInterface;
use Modules\Shared\Controllers\ApiController;
class Advertisement extends ApiController
{
/**
* index function
* #method : GET
*/
public function index()
{
$advertisementEntity = new AdvertisementEntity();
$this->urlQueryParam->dataMap($advertisementEntity->getDataMap());
$advertisementService = Services::advertisementService();
$findAllData = $advertisementService->index($this->urlQueryParam);
return $this->respond([
'data' => $findAllData['data'],
'pager' => $findAllData['pager']
], ResponseInterface::HTTP_OK, lang('Shared.api.receive'));
}
/**
* show function
* #method : GET with params ID
*/
public function show($id = null)
{
$advertisementService = Services::advertisementService();
$findOneData = $advertisementService->show($id);
return $this->respond([
'data' => $findOneData['data'],
'pager' => $findOneData['pager']
], ResponseInterface::HTTP_OK, lang('Shared.api.receive'));
}
/**
* create function
* #method : POST
*/
public function create()
{
$rules = [
'name' => 'required|min_length[3]|max_length[255]',
'link' => 'required',
];
if (!$this->validate($rules)) {
return $this->respond([
'error' => $this->validator->getErrors(),
], ResponseInterface::HTTP_NOT_ACCEPTABLE, lang('Shared.api.validation'));
};
$advertisementEntity = new AdvertisementEntity((array)$this->request->getVar());
$advertisementEntity->enableStatus()->createdAt();
$advertisementService = Services::advertisementService();
$advertisementService->create($advertisementEntity);
return $this->respond([
'insertId' => $advertisementService->getInsertID()
], ResponseInterface::HTTP_CREATED, lang('Shared.api.save'));
}
/**
* update function
* #method : PUT or PATCH
*/
public function update($id = null)
{
if ($this->request) {
//get request from Vue Js
$json = $this->request->getJSON();
if (!isset($id)) {
$id = $json->id;
}
$rules = [
'name' => 'required|min_length[3]|max_length[255]',
'link' => 'required',
];
if (!$this->validate($rules)) {
return $this->respond([
'error' => $this->validator->getErrors(),
], ResponseInterface::HTTP_NOT_ACCEPTABLE, lang('Shared.api.validation'));
}
$advertisementEntity = new AdvertisementEntity((array)$this->request->getVar());
$advertisementEntity->updatedAt();
$advertisementService = Services::advertisementService();
$advertisementService->update($id, $advertisementEntity);
}
return $this->respond([
], ResponseInterface::HTTP_OK, lang('Shared.api.update'));
}
/**
* edit function
* #method : DELETE with params ID
*/
public function delete($id = null)
{
$advertisementService = Services::advertisementService();
$advertisementService->delete($id);
return $this->respond([
], ResponseInterface::HTTP_OK, lang('Shared.api.remove'));
}
}

Related

Validating Nest JSON with Parameters

I am trying to validate a nested JSON object in Laravel. I have created a custom rule to do this however I have an issue currently, I want to be able to pass the object at the current array index to my custom validator:
<?php
namespace App\Http\Requests\App;
use App\Rules\CheckoutDepatureCheck;
use App\Rules\SeatIsAvailable;
use Illuminate\Foundation\Http\FormRequest;
class CheckoutRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
"company" => "required",
"seats" => "required|array",
"seats.*.seat_no" => ['required', new SeatIsAvailable()], // would like to pass seat.* to the constructor of my custom validator here
"seats.*.schedule_id" => "required|numeric",
"seats.*.date" => "required|date"
];
}
}
The point for this is my custom validator needs schedule_id and data as well as the seat_no to successfully validate the request.
How do I do this in Laravel?
You can dynamically add rules depending on the length of the seats' array input
<?php
namespace App\Http\Requests\App;
use App\Rules\CheckoutDepatureCheck;
use App\Rules\SeatIsAvailable;
use Illuminate\Foundation\Http\FormRequest;
class CheckoutRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = [
'company' => 'required',
'seats' => 'required|array',
];
return array_merge($rules, $this->seatsRules());
}
private function seatsRules(): array
{
$rules = [];
foreach ((array) $this->request->get('seats') as $key => $seat) {
$rules["seats.$key.seat_no"] = ['required', new SeatIsAvailable($seat)];
$rules["seats.$key.schedule_id"] = 'required|numeric';
$rules["seats.$key.date"] = 'required|date';
}
return $rules;
}
}

Laravel Job class has incomplete passed model

I am trying to pass a model to job class and when the model is sent to job class, it's incomplete there. What I mean is that it shows only two attributes there instead of 10. $queue_match is complete model (all attributes included, everything is fine) but when it is sent to Job class, then I only see two attributes.
PS: I have included my model at the top of Job class,
$z = dispatch(new updateMatchStatus($queue_match))->onConnection('database')->delay(now()->addMinutes('1'));
namespace App\Jobs;
use App\Http\Controllers\AdminController;
use App\Models\Match;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class updateMatchStatus implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $queue_match;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(Match $queue_match)
{
//
$this->queue_match = $queue_match;
// dd($queue_match);
}
/**
* Execute the job.
*
* #return void
*/
public function handle(Match $queue_match)
{
$now = Carbon::now('PKT');
$now = Carbon::parse($now, 'PKT')->startOfMinute();
// registration close hack, will set Match status to F 30 mins before due time.
$now = $now->addMinutes(30);
$due_time = Carbon::parse($queue_match->due_time, 'PKT');
if ($now->greaterThan($due_time)) {
$queue_match->status = 'F';
// dd($queue_match);
$queue_match->save();
}
}
}
just two attributes in Screenshot
As requested, Here is Match Class
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Match extends Model
{
//
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'platform', 'category', 'map', 'fee', 'is_elite', 'due_time', 'status'
];
public function getPlatformAttribute($value) {
if ($value == 'M') {
return $value ."obile";
}
else {
return $value ."mulator";
}
}
// default attributes
protected $attributes= [
'status' => 'A',
'is_elite' => false
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
// hidden fields for return response
protected $hidden = [
'created_at', 'updated_at',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
];
public function registrations() {
return $this->hasMany('App\Models\Registration', 'match_id', 'id');
}
public function results() {
return $this->hasMany('App\Models\Result', 'match_id', 'id');
}
}
AND, this is how I get the $queue_match which is passed to job.
$queue_match = Match::create([
'category' => $request['category'],
'platform' => $request['platform'],
'map' => $request['map'],
'fee' => $request['fee'],
'due_time' => $request['due_time'],
]);

Access Controller resources based on Laravel/Spatie Permissions

I am working on Laravel passport api in which i am using spatie package for user role's and permission's. I have to perform certain operation ('store','view','update','delete') based on user permission's.
For this purpose i have created a trait and used in controller but it is not working correctly.
On every api request it throw's an exception "This action is unauthorized" either the user has permission or not.
Authorize Trait :
<?php
namespace App;
/*
* A trait to handle authorization based on users permissions for given controller
*/
trait Authorizable
{
/**
* Abilities
*
* #var array
*/
private $abilities = [
'index' => 'view',
'edit' => 'edit',
'show' => 'view',
'update' => 'edit',
'create' => 'add',
'store' => 'add',
'destroy' => 'delete'
];
/**
* Override of callAction to perform the authorization before it calls the action
*
* #param $method
* #param $parameters
* #return mixed
*/
public function callAction($method, $parameters)
{
if( $ability = $this->getAbility($method) ) {
$this->authorize($ability);
}
return parent::callAction($method, $parameters);
}
/**
* Get ability
*
* #param $method
* #return null|string
*/
public function getAbility($method)
{
$routeName = explode('.', \Request::route()->getName());
$action = array_get($this->getAbilities(), $method);
return $action ? $action . '_' . $routeName[0] : null;
}
/**
* #return array
*/
private function getAbilities()
{
return $this->abilities;
}
/**
* #param array $abilities
*/
public function setAbilities($abilities)
{
$this->abilities = $abilities;
}
}
Routes:
Route::middleware('auth:api')->group(function () {
Route::post('user', 'ApiController#user');
Route::post('view_department', 'DepartmentController#index');
Route::post('add_department', 'DepartmentController#store');
Route::post('edit_department', 'DepartmentController#update');
Route::post('delete_department', 'DepartmentController#destroy');
Route::post('/logout', 'ApiController#logout');
}); // auth middleware ends
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\User;
use App\Authorizable;
use Illuminate\Support\Facades\Validator;
use App\Department;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class DepartmentController extends Controller
{
use Authorizable;
//
public function index(Request $request) {
// return response
return response()->json([
'success' => 'You have the permission to view departments!']);
}
//
public function store(Request $request) {
// validate the posted data
$validator = Validator::make($request->all(), [
'name' => 'required|string|unique:departments',
]);
// return errors
if ($validator->fails())
{
return response(['errors'=>$validator->errors()->all()]);
}
$department = new Department;
$department->name = $request->name;
$department->save();
// return response
return response()->json([
'success' => 'Successfully created department!']);
}
}
I am badly stack at it, don't know where i am going wrong. I would highly appreciate if anyone guide me through this.
Thanks,

How to solve Class 'App\Http\Requests\Web\WebRequest' not found

I create a request in App\Http\Requests\Web in which it shows me the error.
Class 'App\Http\Requests\Web\WebRequest' not found
Here is the code of my Request CreateBucket.php:
<?php
namespace App\Http\Requests\Web;
class CreateBucket extends WebRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'bucket_name' => 'required|string|string|max:30',
'bucket_type' => 'required|string|string|max:30',
'bucket_image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg',
];
}
}
And Here is my code of Bucket Controller:
<?php
namespace App\Http\Controllers\Web;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Web\CreateBucket;
use App\Bucket;
class BucketController extends Controller
{
public function index(Request $request)
{
$buckets = Bucket::orderBy('id','ASC')->paginate(10);
return view('buckets.index',compact('buckets',$buckets))
->with('i',($request->input('page',1) - 1) * 10);
}
public function create()
{
return view('buckets.create');
}
public function store(CreateBucket $request)
{
if($request->hasFile('bucket_image')) {
$bucket_image = $request->file('bucket_image');
$bucket_image_name = time().'.'.$bucket_image->getClientOriginalExtension();
$path = public_path('Storage/BucketImages');
$bucket_image->move($path, $bucket_image_name);
$bucket_image = 'Storage/BucketImages/'.$bucket_image_name;
} else {
$bucket_image = NULL;
}
$category = Category::create([
'bucket_name' => $request->input('bucket_name'),
'bucket_image'=> $bucket_image,
'bucket_type' => $request->input('bucket_type'),
]);
return redirect()->route('buckets.index')
->with('success','Bucket created successfully');
}
Please Help me to resolve this error. Thanks.
My WebRequest.php is missing in Requests folder that why he gave me this Error.
Here is the WebRequest.php file I created and my issue is resolve.
<?php
namespace App\Http\Requests\Web;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class WebRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}

Laravel 5 FormRequest validator with multiple scenarios

I would like to ask how should I handle validation on multiple scenarios using FormRequest in L5? I know and I was told that I can create saparate FormRequest files to handle different validations but it is very redundant and also noted that I would need to inject it into the controller manually using the use FormRequest; keyword. What did previously in L4.2 is that I can define a new function inside my customValidator.php which then being called during controller validation via trycatch and then the data is being validated by service using the below implementation.
class somethingFormValidator extends \Core\Validators\LaravelValidator
{
protected $rules = array(
'title' => 'required',
'fullname' => 'required',
// and many more
);
public function scenario($scene)
{
switch ($scene) {
case 'update':
$this->rules = array(
'title' => 'required',
'fullname' => 'required',
// and other update validated inputs
break;
}
return $this;
}
}
Which then in my LaravelValidator.php
<?php namespace Core\Validators;
use Validator;
abstract class LaravelValidator {
/**
* Validator
*
* #var \Illuminate\Validation\Factory
*/
protected $validator;
/**
* Validation data key => value array
*
* #var Array
*/
protected $data = array();
/**
* Validation errors
*
* #var Array
*/
protected $errors = array();
/**
* Validation rules
*
* #var Array
*/
protected $rules = array();
/**
* Custom validation messages
*
* #var Array
*/
protected $messages = array();
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
/**
* Set data to validate
*
* #return \Services\Validations\AbstractLaravelValidator
*/
public function with(array $data)
{
$this->data = $data;
return $this;
}
/**
* Validation passes or fails
*
* #return Boolean
*/
public function passes()
{
$validator = Validator::make(
$this->data,
$this->rules,
$this->messages
);
if ($validator->fails())
{
$this->errors = $validator->messages();
return false;
}
return true;
}
/**
* Return errors, if any
*
* #return array
*/
public function errors()
{
return $this->errors;
}
}
and then finally this is how i call the scenarios inside services like this
public function __construct(somethingFormValidator $v)
{
$this->v = $v;
}
public function updateSomething($array)
{
if($this->v->scenario('update')->with($array)->passes())
{
//do something
else
{
throw new ValidationFailedException(
'Validation Fail',
null,
$this->v->errors()
);
}
}
So the problem is now since i have migrated to L5 and L5 uses FormRequest, how should I use scenario validation in my codes?
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class ResetpasswordRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'login_email' => 'required',
'g-recaptcha-response' => 'required|captcha',
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'g-recaptcha-response.required' => 'Are you a robot?',
'g-recaptcha-response.captcha' => 'Captcha session timeout'
];
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = array(
//scenario rules
);
break;
}
return $this;
}
}
also how should I call it in the controller?
public function postReset(ResetpasswordRequest $request)
{
$profile = ProfileService::getProfileByEmail(Request::input('login_email'));
if($profile == null)
{
$e = array('login_email' => 'This email address is not registered');
return redirect()->route('reset')->withInput()->withErrors($e);
}
else
{
//$hash = ProfileService::createResetHash($profile->profile_id);
$time = strtotime('now');
$ip = Determinator::getClientIP();
MailProcessor::sendResetEmail(array('email' => $profile->email,
'ip' => $ip, 'time' => $time,));
}
}
I believe the real issue at hand is everything is validated through the form request object before it reaches your controller and you were unable to set the appropriate validation rules.
The best solution I can come up with for that is to set the validation rules in the form request object's constructor. Unfortunately, I am not sure how or where you are able to come up with the $scene var as it seems to be hard-coded in your example as 'update'.
I did come up with this though. Hopefully reading my comments in the constructor will help further.
namespace App\Http\Requests;
use App\Http\Requests\Request;
class TestFormRequest extends Request
{
protected $rules = [
'title' => 'required',
'fullname' => 'required',
// and many more
];
public function __construct()
{
call_user_func_array(array($this, 'parent::__construct'), func_get_args());
// Not sure how to come up with the scenario. It would be easiest to add/set a hidden form field
// and set it to 'scene1' etc...
$this->scenario($this->get('scenario'));
// Could also inspect the route to set the correct scenario if that would be helpful?
// $this->route()->getUri();
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return $this->rules;
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = [
//scenario rules
];
break;
}
}
}
You can use laratalks/validator package for validation with multiple scenarios in laravel. see this repo

Resources