Automatically exclude from query (like softDeletes) in Laravel? - laravel

So, I have an 'is_abandoned' boolean on one of my tables, where if it's true, I'd like the model to be automatically excluded from any query - just like softDeletes does.
Is there something I can set in the model that'd achieve this? I'm leaning towards maybe a mutator?

These are called Global Query Scopes.
Writing a global scope is simple. Define a class that implements the Illuminate\Database\Eloquent\Scope interface. This interface requires you to implement one method: apply. The apply method may add where constraints to the query as needed:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class AgeScope 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('age', '>', 200);
}
}
To assign a global scope to a model, you should override a given model's boot method and use the addGlobalScope method:
<?php
namespace App;
use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
}
}

You can just exclude the record using eloquent where function
$lists = List::where('is_abandoned',false);

Here's an example:
Create a function within the Eloquent
class User extends Eloquent {
public function scopeAbandoned($query)
{
return $query->where('is_abandoned', '=', 1/*true*/);
}
}
Then simply use it like this:
$approvedPosts = Post::abandoned()-><put_your_own_condition>;
For detailed info read Eloquent Query Scopes.
No repeated WHERE function calls. Hope It works.

Related

Laravel model - CRUD only with records where one column = certain value?

Suppose i have a laravel model that works with a certain (MySQL) database table.
Inside of this table there is a column named 'administration'. (example: could be 1, 2 or 3)
At the moment in my controller im using that modal with eloquent and inside of my eloquent statement i ask only to work with records (crud style) that have administration 2 for instance.
I think it should be possible to add 'where administration = x' to my model instead of my controller so that if i use the model in my controller i would just be dealing with records that have that administration set.
Is this possible? I have tried googling it but could not find an answer yet.
Laravel uses global scopes for this.
You would create a scope for the administration:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AdministrationScope 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('administration', '=', 2);
}
}
And then you add it as a global scope to the model
<?php
namespace App\Models;
use App\Scopes\AdministrationScope;
use Illuminate\Database\Eloquent\Model;
class YourModel extends Model
{
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::addGlobalScope(new AdministrationScope);
}
}

Laravel authorization/policy prevents access for everyone

I found similar title and similar asked question on this website when I was researching to solve the problem. But none of posted answers helped me. This question might be duplicated but I could not solve the problem using existing questions on StackOverflow.
I'm trying to prevent access to users who are not logged in OR who are not member of "School" model!
In "web.php" file I used "middleware("auth")" to prevent access to users who are not logged in.
Now I created a "Policy" named "SchoolPolicy" to prevent access to users who are not member of "Schools" database/model.
When I call "view" method from SchoolPolicy, it prevents access for all authorized and unauthorized users!
I also checked and I realized "School" model returns "null" when I try to catch "user_id" foreign key from "schools" table.
The below piece of code is the way I created "Schools" table using Migration:
Schema::create('schools', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('school_name');
$table->string('school_address')->nullable();
$table->string('school_email');
$table->string('school_phone')->nullable();
$table->string('url');
$table->longText('descriptions')->nullable();
$table->timestamps();
});
This is the route to view any school which is created by any user (URL can be dynamic)
Route::group(['middleware' => 'auth'], function () {
Route::get('/schools/{url}', [ViewSchool::class, 'index'])->name('yourschool.show');
});
And this is "School" model. I used php artisan make:model School command to create this model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\Auth;
class School extends Model{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_id',
'school_name',
'school_address',
'school_email',
'school_phone',
'url',
'descriptions'
];
}
In this section I created School Policy. However I used Laravel 8 but I also registered created Policy manually
SchoolPolicy
<?php
namespace App\Policies;
use App\Models\School;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class SchoolPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* #param \App\Models\User $user
* #return mixed
*/
public function viewAny(User $user)
{
//
}
/**
* Determine whether the user can view the model.
*
* #param \App\Models\User $user
* #param \App\Models\School $school
* #return mixed
*/
public function view(User $user, School $school)
{
return $user->id == $school->user_id;
}
}
In AuthServiceProvider.php I registered SchoolPolicy like this:
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use App\Models\User;
use App\Models\School;
use App\Policies\SchoolPolicy;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
School::class => SchoolPolicy::class
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
}
}
"ViewSchool.php" file where I want to use authorize method:
<?php
namespace App\Http\Controllers\Schools;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\School;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class ViewSchool extends Controller
{
public function index (School $school) {
$this->authorize('view', $school);
return view('layouts.viewschool');
}
}
I tried many ways to solve the problem, but none of them properly worked:
First Try:
public function index (School $school) {
$this->authorize('view', $school);
}
Second Try:
public function index () {
$this->authorize('view', School::class);
}
I even tried to print any output from School model but I receive "null":
public function index (School $school) {
dd($school->user_id);
}
I followed all tutorials on YouTube and official Laravel website, but in my examples I gave you, authorization doesn't work properly.
Please help me to solve this problem.
Thank you

only approved column returned back on any relationship

i have table products.
i need make something in model only return product where approved is equal to 1
this my schema
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('description');
$table->unsignedInteger('quantity');
$table->unsignedInteger('subcategory_id')
->references('id')->on('subcategories')->onDelete('cascade');
$table->decimal('price');
$table->decimal('discount_price');
$table->decimal('super_discount')->nullable();
$table->string('cover');
$table->unsignedInteger('brand_id')
->references('id')->on('brands')->onDelete('cascade');
$table->unsignedInteger('category_id')
->references('id')->on('categories');
$table->string('color');
$table->string('size_id')->references('id')->on('sizes')->nullable();
$table->decimal('rate');
$table->enum('made_in',['turkey','china','egypt']);
$table->string('serial');
$table->boolean('approved')->default(0);
$table->timestamps();
$table->unique(['name','size_id','color']);
});
i hope this is valid.
You can make use of Global Scopes
Global scopes allow you to add constraints to all queries for a given
model. Laravel's own soft deleting functionality utilizes global
scopes to only pull "non-deleted" models from the database. Writing
your own global scopes can provide a convenient, easy way to make sure
every query for a given model receives certain constraints.
Writing Global Scopes Writing a global scope is simple. Define a class
that implements the Illuminate\Database\Eloquent\Scope interface. This
interface requires you to implement one method: apply. The apply
method may add where constraints to the query as needed:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class ApprovedScope 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)
{
return $builder->where('approved', 1);
}
}
There is not a predefined folder for scopes in a default Laravel
application, so feel free to make your own Scopes folder within your
Laravel application's app directory.
Applying Global Scopes To assign a global scope to a model, you should
override a given model's boot method and use the addGlobalScope
method:
<?php
namespace App;
use App\Scopes\ApprovedScope;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new ApprovedScope);
}
}
After adding the scope, a query to Product::all() will produce the
following SQL:
select * from `products` where `approved` = 1
Docs

laravel policy not called

Does documention hides something, or there is something hidden?
created with
php artisan make:policy AdvertisementPolicy --model=Advertisement
class AdvertisementPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any advertisements.
*
* #param \App\User $user
* #return mixed
*/
public function viewAny(User $user)
{
return false;
}
public function view(User $user, Advertisement $advertisement)
{
return false;
}
model was created with cli too
namespace App;
class Advertisement extends Model
{
Registered through:
use App\Advertisement;
use App\Policies\AdvertisementPolicy;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Advertisement::class => AdvertisementPolicy::class,
Is here any additional steps to fulfill this policy registration with laravel 6?
There is no something hidden in documentation. You just don't read the documentation carefully.
Please take a look at the Authorizing Actions Using Policies section.
Your policy is never called, because you don't use it anywhere in your code. Atleast, if you need to run your policy for your controller resources, you need to write something like this:
<?php
namespace App\Http\Controllers;
use App\Advertisement;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class AdvertisementController extends Controller
{
public function __construct()
{
$this->authorizeResource(Advertisement::class, 'advertisement');
}
}

Global scope preventing DB seeding

I am using Laravel spark, and I am restricting team access to models by employing a scope that is implemented in a trait
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 );
}
}
my issue is that when I run the DB seeder it fails because there is no user to auth against.
The seeder doesn't have any methods to allow me to log in a particular user.
is there a way to disable the global scope whilst seeding, or any other solution for that matter?
THanks
If you're calling the model inside the seed, you can just call ->withoutGlobalScopes() first
more on that here https://laravel.com/docs/5.7/eloquent#query-scopes at the 'Removing Global Scopes' section.

Resources