How can I sanitize laravel Request inputs? - php

I have MyRequest.php class extending App\Http\Requests\Request. I want to trim() every input before validation because an e-mail with a space after it does not pass validation.
However sanitize() was removed from src/Illuminate/Foundation/Http/FormRequest.php

Create an abstract SanitizedRequest class that extends the usual Request class.
YourRequest class should extend your SanitizedRequest abstract class.
Your SanitizedRequest class overrides Request::all() as like so...
namespace App\Http\Requests\Forms;
use App\Http\Requests\Request;
abstract class SanitizedRequest extends Request{
private $clean = false;
public function all(){
return $this->sanitize(parent::all());
}
protected function sanitize(Array $inputs){
if($this->clean){ return $inputs; }
foreach($inputs as $i => $item){
$inputs[$i] = trim($item);
}
$this->replace($inputs);
$this->clean = true;
return $inputs;
}
}
Then a normal CustomRequest, but extend SanitizedRequest instead of laravel's Request class
class ContactRequest extends SanitizedRequest{
public function authorize(){ return true; }
public function rules(){ return []; }
}

I just came across for the same problem.
I'd like to show you another way of doing it without extends but with traits. ( I will take the Example Classes from Tarek Adam ).
PHP Traits are like functions which will be injected into the used class. The one main difference is that a Trait doesn't need any dependency like a extends do. This means you can use a trait for more then just one class e.x. for Controllers, Requests and whatever you like.
Laravel provides some traits in the BaseController, we can do the same.
How to do it with a trait
Create a trait as file in \App\Traits\SanitizedRequest.php. You can create it anywhere it doesn't matter really. You have to provide the correct namespace for sure.
namespace App\Trait;
trait SanitizedRequest{
private $clean = false;
public function all(){
return $this->sanitize(parent::all());
}
protected function sanitize(Array $inputs){
if($this->clean){ return $inputs; }
foreach($inputs as $i => $item){
$inputs[$i] = trim($item);
}
$this->replace($inputs);
$this->clean = true;
return $inputs;
}
}
In your Request you can use the trait with use SanitizedRequest keyword.
namespace App\Http\Requests\Forms;
use App\Http\Requests\Request;
use App\Trait\SanitizedRequest; // Import the Trait
class ContactRequest extends Request {
use SanitizedRequest; // This line adds all the Trait functions to your current class
public function authorize(){ return true; }
public function rules(){ return []; }
}

In Laravel 9 (not sure about older versions) you can extend \Illuminate\Foundation\Http\Middleware\TransformsRequest middleware and do apply your logic there. So lets say you need to encode your input with htmlemntities() function, create a middleware
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
class TransformsRequest extends \Illuminate\Foundation\Http\Middleware\TransformsRequest
{
/** #inheritDoc */
protected function transform($key, $value)
{
return is_string($value) ? htmlentities($value, ENT_QUOTES, "UTF-8") : $value;
}
}
Dont forget to apply this middleware globally in App\Http\Kernel.php
protected $middleware = [
// other middlewares...
\App\Http\Middleware\TransformsRequest::class,
];
This middleware will encode each value that comes from input for all requests.

Related

How to correctly extend and use other interfaces?

I'm trying to make use of a base interface for all my other interfaces as follows:
Base interface
<?php
namespace App\Repositories\Data;
interface IDataRepository
{
public function getAll();
public function getById($id);
public function create($model);
public function update($model);
public function delete($id);
}
Implemented base interface
<?php namespace App\Repositories\Data;
use Illuminate\Database\Eloquent\Model;
class DataRepository implements IDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
The interface where i'm trying to make use of the base interface
<?php
namespace App\Repositories;
use App\Repositories\Data\IDataRepository;
interface ITestRepository extends IDataRepository
{
}
implementation
<?php namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
}
In my controller i'm trying to just call test repository so i can use all the base repository functions:
class TestController extends Controller
{
protected $testRepository;
public function __construct(Test $test)
{
$this->testRepository = new TestRepository($test);
}
public function index()
{
$data['testData'] = $this->testRepository->getAll();
return view('test', $data);
}
}
But i get the following error:
Class App\Repositories\TestRepository contains 5 abstract methods and
must therefore be declared abstract or implement the remaining methods
My application works fine if i only make use of my base interface and pass through a model. What would be the correct way to share functions from my base interface across all my other interfaces, so as to prevent code duplication? I appreciate any help.
I think that a Trait which will contains all methods of your interface declaration is the best choice. Something like (not sure about logic):
namespace App\Repositories;
trait TDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
And then just use it for classes with base interface:
namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
use TDataRepository;
}
Also there are some other options:
abstract class with methods for base interface but it not so flexible like trait,
composition but you should change base idea and create a new entity for composition.
<?php
namespace App\Repositories;
use App\Interfaces\ITestRepository;
class TestRepository implements ITestRepository
{
public function getAll()
{
// TODO: Implement getAll() method.
}
public function getById($id)
{
// TODO: Implement getById() method.
}
public function create($model)
{
// TODO: Implement create() method.
}
public function update($model)
{
// TODO: Implement update() method.
}
public function delete($id)
{
// TODO: Implement delete() method.
}
}
Class must be declared abstract or implement methods 'getAll', 'getById', 'update', 'create', 'delete'
So All the method is by default abstract method in interface and you have to define all method in this class.
The class TestRepository should not implement any interface, but extend DataRepository:
<?php namespace App\Repositories;
use App\Repositories\Data\DataRepository;
class TestRepository extends DataRepository
{
}
DataRepository contains already the implementation of the interface IDataRepository. When you create a class implementing ITestRepository you will have to define the implementation of all the methods in the interface (which are the same as the base interface, in your case).

How to register a default Observer for every Model instance with Laravel

Laravel 5.1
I'm trying to register a single model observer for every Model that extends my AbstractModel (who are extending Illuminate\Database\Eloquent\Model).
The problem is my GenericModelObserver can't listen to events fired by Models inheriting AbstractModel.
Let me show what I did so far.
A Service Provider was created and put on the last position of the providers array inside config/app.php
<?php
// app/Providers/ObserverServiceProvider.php
namespace App\Providers;
use App\Models\Quotation;
use App\Models\AbstractModel;
use App\Observers\QuotationObserver;
use App\Observers\GenericModelObserver;
use Illuminate\Support\ServiceProvider;
class ObserverServiceProvider extends ServiceProvider
{
public function boot()
{
AbstractModel::observe(GenericModelObserver::class);
Quotation::observe(QuotationObserver::class);
}
public function register()
{
}
}
Then I have my plain simple GenericModelObserver
<?php
// app/Observers/GenericModelObserver.php
namespace App\Observers;
use App\Models\AbstractModel;
class GenericModelObserver
{
public function saving(AbstractModel $model)
{
return $model->valid();
}
}
The Abstract Model
<?php
// app/Models/AbstractModel.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AbstractModel extends Model
{
// ...
}
My Quotation Model
<?php
// app/Models/Quotation.php
namespace App\Models;
class Quotation extends AbstractModel
{
// ...
}
When Quotation is saved, the GenericModelObserver can't listen to the saving event or any other event.
The same applies for other Models that don't have a specific Model Observer.
Is this the right strategy? I would not like to bind a observer to every model through the boot method.
Instead of extending model - write your own trait which will work as observer.
Below I wrote some basic trait:
<?php
namespace App\YourPackage\Traits;
use Illuminate\Database\Eloquent\Model;
trait Observable
{
public static function bootObservable()
{
static::updating(function (Model $model) {
dd('updating');
});
}
}
and use it by typing use Observable; in your model class.
Also for your learning take a note how traits is booting: You have to put boot[TraitClassName] method into trait, to boot it properly.
Never write boot method inside your trait, it's dangerous!
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Observers\TeamObserver;
class Team extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
self::observe(TeamObserver::class);
}
}
Why not simply extend a parent class say BaseObserver
I have something similar in my caching system
<?php namespace App\Observers;
class BaseObserver {
public function saving($model)
{
//do your thing here that apply to all observers, like caching
}
}
Then in your Observers
<?php namespace App\Observers;
class Quotation extends BaseObserver{
//you can override any of the methods if you wish
}
Update the boot method in your AppServiceProvider to the following:
public function boot()
{
# Register all model observers
$filesInFolder = \File::files(app_path('/Observers'));
foreach($filesInFolder as $path) {
$observerClassName = pathinfo($path)['filename'];
$className = str_replace('Observer', '', $observerClassName);
$observerClassName = 'App\\Observers\\' . $observerClassName;
$className = 'App\\' . $className;
$className::observe($observerClassName);
}
}
Models should follow this format:
App\User
Observers should follow this format:
App\Observers\UserObserver
When models are in the 'models' folder:
Swap this $className = 'App\\' . $className; for this $className = 'App\\Models\\' . $className;
In your parent model you can do something like that
/**
* If true will attach the observers of the parent class
* #var bool
*/
protected $shouldAttachParentObservers = true;
public static function boot()
{
$instance = new static;
$instance->attachParentObservers();
parent::boot();
}
public function attachParentObservers() {
$parentClass = get_parent_class($this);
if(!empty($parentClass) && $this->shouldAttachParentObservers) {
$eventObservers = [];
foreach ($this->getObservableEvents() as $event) {
$eventObservers[$event] = ($this::$dispatcher->getListeners("eloquent.{$event}: {$parentClass}"));
foreach ($eventObservers[$event] as $observer) {
$eventName = "eloquent.{$event}: {$this::getClassName()}";
$this::$dispatcher->listen($eventName, $observer);
}
}
}
}
/**
* You may use different way to find the class name
*/
public static function getClassName() {
return static::class;
}

How to call a 'parent' trait method in PHP?

This is my code
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller {
use ResetsPasswords;
public function postReset(Request $request){
// do some stuff
// ...
return parent::postReset($request); // <-here is the problem
}
The method postReset is present in ResetsPasswords, but the code I've written is looking for this method within the Controller class.
Any ideas?
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller {
use ResetsPasswords {
ResetsPassword::postReset as traitPostReset;
};
public function postReset(Request $request){
// do some stuff
// ...
return $this->traitPostReset($request);
}
}
The reason why parent::postReset($request) issues a fatal error is because its parent Controller class hasn't postReset method. The trait isn't considered as a parent in that case even if it has an implementation of postReset() method.
To work-around this, you can give it a different name:
class PasswordController extends Controller
{
use ResetsPasswords;
public function postResetPassword(Request $request) // <- giving it a different name
{
// do some stuff
// ...
return $this->postReset($request); // and calling ResetsPasswords::postReset
}

Laravel 5 Resolving dependencies in ServiceProvider

I have a class which acts like a storage (add/get item). I try to bind it as a singleton in one service provider, and resolve it in another's boot method.
The code is changed for simplicity.
app/Providers/BindingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider as ServiceProvider;
class MyBindingFacade extends Facade {
public static function getFacadeAccessor() {
return 'my.binding';
}
}
class MyBinding {
protected $items = [];
public function add($name, $item) {
$this->items[$name] = $item;
}
public function get($name) {
return $this->items[$name];
}
public function getAll() {
return $this->items;
}
}
class BindingProvider extends ServiceProvider {
public function register() {
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
}
public function provides() {
return [
'my.binding',
];
}
}
app/Providers/ResolvingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider as ServiceProvider;
use App\Providers\MyBinding;
class ResolvingProvider extends ServiceProvider {
public function boot(MyBinding $binding) {
$binding->add('foo', 'bar');
// $manual = $this->app->make('my.binding');
// $manual->add('foo', 'bar');
}
public function register() {}
}
app/Http/Controllers/WelcomeController.php
<?php
namespace App\Http\Controllers;
use App\Providers\MyBindingFacade;
class WelcomeController extends Controller {
public function index()
{
dd(MyBindingFacade::getAll()); // debug items
}
}
When I try to debug MyBinding state in my WelcomeController I'm getting empty item array. However, if I uncomment $manual part from my ResolvingProvider it returns an array containing 'foo' => 'bar'. Does it mean IoC resolution is broken in ServiceProvider::boot() method or am I misusing Laravel functionality?
Laravel version: 5.0.28
UPDATE: Added code sample from WelcomeController.
With this:
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
You're saying: my.binding is a singleton and resolves to an instance of App\Providers\MyBinding.
That doesn't mean that App\Providers\MyBinding is registered as singleton too. What you should do instead is this:
$this->app->singleton('App\Providers\MyBinding');
$this->app->bind('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
Because the Facade binding uses $app->make() you should get the same instance you registered with $this->app->singleton() right above.
In the first example you are not using the Facade, you should be using:
use App\Providers\MyBindingFacade as MyBinding;
Which will in fact call make it using 'my.binding'.

Issue with Facade and injected dependency in Laravel 4

I am having an issue getting a Facade to work properly with a dependency injected into the underlying class.
I have a class called 'Listing'. It has one dependency called 'AdvertRepository' which is an interface and a class called EloquentAdvert which implements the interface. The code for these three classes is here:
// PlaneSaleing\Providers\Listing.php
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
class Listing {
protected $advert;
public function __construct (AdvertRepository $advert_repository) {
$this->advert = $advert_repository;
}
public function test() {
$this->advert->test();
}
public function test2() {
echo "this has worked";
}
}
// PlaneSaleing\Repositories\Advert\AdvertRepository.php
<?php namespace PlaneSaleing\Repositories\Advert;
interface AdvertRepository {
public function test();
}
// PlaneSaleing\Repositories\Advert\EloquentAdvert.php;
<?php namespace PlaneSaleing\Repositories\Advert;
class EloquentAdvert implements AdvertRepository {
public function test() {
echo 'this has worked';
}
}
I have then created a service provider called ListingServiceProvider.php, which has the following code:
// PlaneSaleing/Providers/ListingServiceProvider.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
}
}
I also added this to the ServiceProviders array in app.php
Now, if I inject Listing as a dependency into a controller and call the test method (as shown below) Laravel correctly detects the dependency, instantiates EloquentAdvert via its binding and displays 'this has worked'.
// Controllers/TestController.php
use PlaneSaleing\Providers\Listing;
class TestController extends BaseController {
protected $listing;
public function __construct(Listing $listing) {
$this->listing = $listing;
}
public function test1() {
$this->listing->test();
}
}
Now, I then created a facade for Listing. I added a new facade as follows and added an alias in app.php:
// PlaneSaleing\Providers\ListingFacade.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\Facades\Facade;
class ListingFacade extends Facade {
protected static function getFacadeAccessor() {
return 'Listing';
}
}
I also added the following new lines to ListingServiceProvider.php:
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
use PlaneSaleing\Repositories\Advert\EloquentAdvert;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
// New lines...
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
}
}
NOW...if I call Listing::test(), I get the following error: Cannot instantiate interface PlaneSaleing\Repositories\Advert\AdvertRepository.
If I call Listing::test2() , I get 'this has worked' so it seems the Facade is working correctly.
It seems that when accessing Listing via its Facade the binding between AdvertRepository and EloquentAdvert doesnt work. I have looked at my code in the ServiceProvider thinking it was the issue, but I cant figure it out.
Both the Facade and binding work when tested individually but not when both are used at the same time.
Any ideas???
OK, So I have figured it out...For those who run into a similar problem...
The offending statement was in ListingServiceProvider.php which read:
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
The error is the new AdvertRepository statement. The reason being is that, we are telling php to directly instantiate the interface 'AdvertRepository'. Instead, we need to tell Laravel to instantiate the appropriate implementation of the 'AdvertRepository' interface. To do that, we use App::make('AdvertRepository'). That way, Laravel uses the binding previously declared to instantiate the correct implementation.
If your constructor is not being inject with a class, you must tell Laravel what class will be used when it needs to instantiate a particular interface:
Put this in your filters or bindings file:
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', function()
{
return new PlaneSaleing\Repositories\Advert\EloquentAdvert;
});

Categories