Laravel class#method - laravel

This is Laravel 5.4 and I'm new to the framework.
I'm trying to create a custom validator but I want to use my own class rather than having a load of logic in App\Providers
However, when I call the class like this: 'MultiDateFieldValidator#validate' I get an error: Class MultiDateFieldValidator does not exist
If I use the full namespace it works: 'App\Validators\MultiDateFieldValidator#validate'
So my question is whether its normal to have to use the full namespace in the class#method call? Or whether you have to do some Laravel magic to make it recognise the namespace at the top of the file?
Code is below.
Thanks!
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use App\Validators\MultiDateFieldValidator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('foo', 'MultiDateFieldValidator#validate' );
}

Related

PHPUnit tests failing after adding a ViewServiceProvider for sharing data to views

I am relatively new to testing so please forgive me if this a stupid question.
All my tests are failing after I added a ViewServiceProvider to share data with the views.
The error is:
Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 1 no such table: policies (SQL: select "name", "slug" from "policies")
My tests are making use the the refresh database trait:
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
...
}
Here is an example of the ViewServiceProvider:
namespace App\Providers;
use App\Models\Policy;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
public function register(): void{...}
public function boot(): void
{
view()->share('policies', Policy::all(['name', 'slug']));
}
}
Every works when browsing the site on front end. Am i missing something? Why are the tests failing?
I will appreciate suggestions on how to make the tests pass.
Edit
It makes total sense what #Donkarnash said but it's also confusing because according to the docs:
So, what if we need to register a view composer within our service provider? This should be done within the boot method. This method is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework
See https://laravel.com/docs/8.x/providers#the-boot-method
I found the solution. Creating a view composer solved the problem.
namespace App\Http\View\Composers;
use App\Models\Policy;
use Illuminate\View\View;
class PolicyComposer
{
public function compose(View $view): void
{
$view->with('policies', Policy::all(['name', 'slug']));
}
}
Then I referenced the composer in the ViewServiceProvider
namespace App\Providers;
use App\Models\Policy;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
public function register(): void{...}
public function boot(): void
{
view()->composer('*', PolicyComposer::class);
}
}

Override vendor model in laravel 6.X

I want to override the model inside of vendor. I tried bellow code. But not working.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
$this->app->bind('VendorName\Models\User', 'App\Models\User');
}
}
Extending model is not an option, as i have to override all controller change model path and write all methods again, its not worth it.
Binding a class in the container like this isn't going to override direct references to the class you're trying to override if the object isn't being resolved using the service container.
So for example, something like $user = new \VendorName\Models\User; isn't going to be affected because it's simply not using the container.
I think the only sensible solution is to refactor your code so you're using a class that extends the base User class.

Call Auth:user() Laravel in extend Controller

I create my controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ResponsaveisController extends InternoController
{
}
I try to access Auth::user() multiple forms, all times return NULL
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Auth;
class InternoController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
var_dump(Auth::user());
}
}
Would anyone have any suggestions on how to solve this issue?
This is taken directly from the Laravel docs
In previous versions of Laravel, you could access session variables or the authenticated user in your controller's constructor. This was never intended to be an explicit feature of the framework. In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet
You can read about it here as it provides an alternative solution.

Laravel Testing/phpunit, Too few arguments passed when using dependency injection

I'm trying to create a simple test with Laravel. My test code is as below;
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Http\Controllers\Abc\AbcController;
class AbcTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
private $abcController;
public function __construct (AbcController $abcController) {
$this->abcController = $abcController;
}
public function testExample()
{
$this->assertTrue(true);
}
However, when i run the test, i'm hitting this error,
PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function Tests\Feature\abc::__construct(), 0 passed in /var/www/nex/backend/vendor/phpunit/phpunit/src/Framework/TestSuite.php on line 151 and exactly 1 expected in /var/www/nex/backend/tests/Feature/abc.php:28
I've been using this method of performing dependency injections for the rest of my project. I'm not sure why its not working on this particular code.
All help is appreciated.
Thanks!
Check https://laravel.com/docs/5.8/testing you should not use Dependency Injection on controller. Instead you should call the endpoint.
Example
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Http\Controllers\Abc\AbcController;
class AbcTest extends TestCase
{
public function testExample()
{
$response = $this->get('/url');
$response->assertOk();
}
}

Enforce Global Scope Across all models

we are developing an application based on Laravel Spark. as part of this we want to tie resources to a specfic team.
I know that we can add a global scope such as:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class TeamScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('team_id', '=',Auth()->user()->currentTeam->id );
}
}
but according to the docs we have to add that to each model that we want to restrict like so:
protected static function boot()
{
parent::boot();
static::addGlobalScope(new TeamScope);
}
my issue with this is that it will be possible to create future models and forget to apply this code. Which could give us a security hole?
is there any way to enforce the scope across the board?
I am not sure if there's a way to globally add the Scope.
In my particular application, we have had to add more responsiblities to our Models. So we created a BaseModel class that extends Laravel's Illuminate\Database\Eloquent\Model.
All new Models then extends the BaseModel instead of Laravel's one.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BaseModel extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new TeamScope);
}
}
For example:
<?php
namespace App;
class Attribute extends BaseModel
{
}
You could also have a trait that you can just use to add this scope to your Model. For example:
trait HasTeamScope
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new TeamScope);
}
}
}
... and then you can easily re-use that in your Model.
For example:
<?php
namespace App;
class Attribute extends BaseModel
{
use HasTeamScope;
}
Now, based on your question, you might also forget to extend the BaseModel in the first instance or add the Trait in the second one whenever you create a new model.
To solve this, you could easily create a new command to produce models that will use your own stub (which extends the BaseModel or adds the trait whenever you create a new model)
You could create your own base model with the desired global scope that future models would extend.
You should create trait with boot function. Trait named BelongsToTeam.
And in all models add only: use BelongsToTeam;

Resources