国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Table of Contents
What is query scope?
Local query scope
Global query scope
#How to create a global query scope
#Apply global query scope
#Anonymous global query scope
#Ignore global query scope
#Precautions for global query scope
Test local query scope
Test range in controller
Conclusion
Home Backend Development PHP Tutorial Learn to master Query Scopes in Laravel

Learn to master Query Scopes in Laravel

Mar 06, 2025 am 02:28 AM

<p><img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/174119929536701.jpg" class="lazy" alt="Learn to master Query Scopes in Laravel"></p> <p>When building a Laravel application, you may need to write queries with constraints that are used in multiple places throughout the application. Maybe you are building a multi-tenant application and you have to constantly add <code>where</code> constraints to the query to filter by user's team. Or maybe you are building a blog and you have to constantly add <code>where</code> constraints to the query to filter if the blog post has been published. </p> <p>In Laravel, we can use query scopes to help us keep these constraints neatly in one place and reuse them. </p> <p>In this article, we will study the local query scope and the global query scope. We will learn the difference between the two, how to create your own query scope, and how to write tests for them. </p> <p>After reading this article, you should be able to use query scopes confidently in your Laravel application. </p> <h1 id="What-is-query-scope">What is query scope? </h1> <hr> <p>Query scope allows you to define constraints in Eloquent queries in a reusable way. They are usually defined as methods on the Laravel model, or as classes that implement <code>IlluminateDatabaseEloquentScope</code> interfaces. </p> <p> Not only are they ideal for defining reusable logic in one place, but they can also make your code more readable by hiding complex query constraints after simple function calls. </p> <p>Query ranges are divided into two types: </p> <ul> <li>Local Query Ranges - You must manually apply these ranges to your query. </li> <li>Global Query Scopes - By default, these ranges are applied to all queries on the model, provided that the query is registered. </li> </ul> <p>If you have ever used the "soft delete" feature built in Laravel, you may have used the query scope unknowingly. Laravel uses local query scope to provide you with methods like <code>withTrashed</code> and <code>onlyTrashed</code> on the model. It also uses the global query scope to automatically add <code>whereNull('deleted_at')</code> constraints to all queries on the model so that soft deleted records are not returned by default in the query. </p> <p>Let's see how to create and use local query scopes and global query scopes in a Laravel application. </p> <h1 id="Local-query-scope">Local query scope</h1> <hr> <p> Local query scope is defined as a method on the Eloquent model, allowing you to define constraints that can be applied manually to model queries. </p> <p>Suppose we are building a blog application with an admin panel. In the admin panel, we have two pages: one for listing published blog posts and the other for listing unpublished blog posts. </p> <p>We assume that the blog post is accessed using the <code>AppModelsArticle</code> model and that the database table has an empty <code>published_at</code> column to store the publishing time of the blog post. If <code>published_at</code> is listed in the past, the blog post is considered to have been published. If <code>published_at</code> is listed in the future or is <code>null</code>, the blog post is deemed to be unpublished. </p> <p>To obtain published blog posts, we can write the following query: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->where('published_at', '<', now()) ->get();</code></pre> <p>To obtain unpublished blog posts, we can write the following query: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; use Illuminate\Contracts\Database\Eloquent\Builder; $unpublishedPosts = Article::query() ->where(function (Builder $query): void { $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }) ->get();</code></pre> <p>The above query is not particularly complicated. But, suppose we use them in multiple places throughout the application. As the number of occurrences increases, the possibility of us making mistakes or forgetting to update the query in one place is increasing. For example, developers may accidentally use <code>>=</code> instead of <code><</code> to query published blog posts. Alternatively, the logic to determine if a blog post has been published may change and we need to update all queries. </p> <p>This is where query scopes are very useful. So let's organize our queries by creating a local query scope on the <code>AppModelsArticle</code> model. </p> <p> Local query scopes are defined by creating a method that starts with <code>scope</code> and ends with the expected name of the scope. For example, a method named <code>scopePublished</code> will create a <code>published</code> range on the model. This method should accept an <code>IlluminateContractsDatabaseEloquentBuilder</code> instance and return a <code>IlluminateContractsDatabaseEloquentBuilder</code> instance. </p> <p>We add both ranges to the <code>AppModelsArticle</code> model: </p> <pre class="brush:php;toolbar:false"><code>declare(strict_types=1); namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; final class Article extends Model { public function scopePublished(Builder $query): Builder { return $query->where('published_at', '<', now()); } public function scopeNotPublished(Builder $query): Builder { return $query->where(function (Builder $query): Builder { return $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }); } // ... }</code> </p> <p> As we saw in the example above, we moved the <code>where</code> constraint from the previous query into two separate methods: <code>scopePublished</code> and <code>scopeNotPublished</code>. We can now use these ranges in our query like this: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->published() ->get(); $unpublishedPosts = Article::query() ->notPublished() ->get();</code></pre> <p>In my personal opinion, I find these queries easier to read and understand. This also means that if we need to write any query with the same constraints in the future, we can reuse these scopes. </p> <h1 id="Global-query-scope">Global query scope</h1> <hr> <p>Global query scope performs functions similar to local query scope. However, it is not applied manually on a query-by-query basis, but automatically applies to all queries on the model. </p> <p> As we mentioned earlier, Laravel's built-in "soft delete" function uses <code>IlluminateDatabaseEloquentSoftDeletingScope</code> global query scope. This range automatically adds <code>whereNull('deleted_at')</code> constraints to all queries on the model. If you are interested in understanding how it works, you can check out the source code on GitHub here. </p> <p>For example, suppose you are building a multi-tenant blog application with an admin panel. You may just want to allow users to view articles that belong to their team. So you might write a query like this: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->where('published_at', '<', now()) ->get();</code></pre> <p>This query is good, but it's easy to forget to add <code>where</code> constraints. If you are writing another query and forgetting to add constraints, you will end up with an error in your application that will allow the user to interact with articles that are not part of their team. Of course, we don't want this to happen! </p> <p>To prevent this, we can create a global scope that we can automatically apply to all our <code>AppModelArticle</code> model queries. </p> <h3 id="How-to-create-a-global-query-scope">#How to create a global query scope</h3> <p> Let's create a global query scope that filters all queries by the <code>team_id</code> column. </p> <p> Please note that for the purposes of this article, we keep the example simple. In a real-life application, you may want to use a more powerful approach to dealing with situations like the user is not authenticated or the user belongs to multiple teams. But for now, let's keep it simple so we can focus on the concept of global query scope. </p> <p>We will first run the following Artisan command in the terminal: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; use Illuminate\Contracts\Database\Eloquent\Builder; $unpublishedPosts = Article::query() ->where(function (Builder $query): void { $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }) ->get();</code></pre> <p> This should have created a new <code>app/Models/Scopes/TeamScope.php</code> file. We will make some updates to this file and then look at the finished code: </p> <pre class="brush:php;toolbar:false"><code>declare(strict_types=1); namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; final class Article extends Model { public function scopePublished(Builder $query): Builder { return $query->where('published_at', '<', now()); } public function scopeNotPublished(Builder $query): Builder { return $query->where(function (Builder $query): Builder { return $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }); } // ... }</code></pre> <p> In the above code example we can see that we have a new class that implements the <code>IlluminateDatabaseEloquentScope</code> interface and has a single method called <code>apply</code>. This is how we define the constraints to apply to model queries. </p> <p>Our global scope is now available. We can add this to any model we want to narrow the query to the user team. </p> <p>Let's apply it to the <code>AppModelsArticle</code> model. </p> <h3 id="Apply-global-query-scope">#Apply global query scope</h3> <p>There are several ways to apply the global scope to the model. The first method is to use the <code>IlluminateDatabaseEloquentAttributesScopedBy</code> attribute on the model: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->published() ->get(); $unpublishedPosts = Article::query() ->notPublished() ->get();</code></pre> <p> Another method is to use <code>booted</code> method in the model's <code>addGlobalScope</code> method: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $articles = Article::query() ->where('team_id', Auth::user()->team_id) ->get();</code></pre> <p> Both methods apply the <code>where('team_id', Auth::user()->team_id)</code> constraints to all queries on the <code>AppModelsArticle</code> model. </p> <p> This means you can now write queries without worrying about filtering by <code>team_id</code> column: </p> <pre class="brush:php;toolbar:false"><code>php artisan make:scope TeamScope</code></pre> <p>If we assume that the user belongs to a team with <code>team_id</code> being <code>1</code>, the following SQL will be generated for the above query: </p> <pre class="brush:php;toolbar:false"><code>declare(strict_types=1); namespace App\Models\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; use Illuminate\Support\Facades\Auth; final readonly class TeamScope implements Scope { /** * Apply the scope to a given Eloquent query builder. */ public function apply(Builder $builder, Model $model): void { $builder->where('team_id', Auth::user()->team_id); } }</code></pre> <p>This is cool, right? ! </p> <h3 id="Anonymous-global-query-scope">#Anonymous global query scope</h3> <p> Another way to define and apply a global query scope is to use an anonymous global scope. </p> <p>Let's update our <code>AppModelsArticle</code> model to use anonymous global scope: </p> <pre class="brush:php;toolbar:false"><code>declare(strict_types=1); namespace App\Models; use App\Models\Scopes\TeamScope; use Illuminate\Database\Eloquent\Attributes\ScopedBy; use Illuminate\Database\Eloquent\Model; #[ScopedBy(TeamScope::class)] final class Article extends Model { // ... }</code></pre> <p> In the above code example, we used the <code>addGlobalScope</code> method to define anonymous global scope in the <code>booted</code> method of the model. <code>addGlobalScope</code> method accepts two parameters: </p> <ul> <li>The name of the scope - If you need to ignore it in your query, you can use this name to refer to the scope </li> <li>Scope Constraints - Define the closure to apply to the constraints </li> </ul> <p> As with other methods, this applies the <code>where('team_id', Auth::user()->team_id)</code> constraint to all queries on the <code>AppModelsArticle</code> model. </p> <p>In my experience, anonymous global scope is not as common as defining global scope in a separate class. But it is beneficial to know that they are available in case of emergencies. </p> <h3 id="Ignore-global-query-scope">#Ignore global query scope</h3> <p> Sometimes you may want to write a query that does not use the global query scope that has been applied to the model. For example, you might be building a report or analysis query that needs to contain all records regardless of the global query scope. </p> <p>If this is the case, you can use one of two methods to ignore the global scope. </p> <p>The first method is <code>withoutGlobalScopes</code>. This method allows you to ignore all global scopes on the model if no parameters are passed to it: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->where('published_at', '<', now()) ->get();</code></pre> <p> Or, if you want to ignore only a given set of global scopes, you can pass the scope name to the <code>withoutGlobalScopes</code> method: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; use Illuminate\Contracts\Database\Eloquent\Builder; $unpublishedPosts = Article::query() ->where(function (Builder $query): void { $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }) ->get();</code></pre> <p> In the example above, we ignore <code>AppModelsScopesTeamScope</code> and another fictional anonymous global scope called <code>another_scope</code>. </p> <p> Or, if you want to ignore only a single global scope, you can use the <code>withoutGlobalScope</code> method: </p> <pre class="brush:php;toolbar:false"><code>declare(strict_types=1); namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; final class Article extends Model { public function scopePublished(Builder $query): Builder { return $query->where('published_at', '<', now()); } public function scopeNotPublished(Builder $query): Builder { return $query->where(function (Builder $query): Builder { return $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }); } // ... }</code></pre> <h3 id="Precautions-for-global-query-scope">#Precautions for global query scope</h3> <p>It is important to remember that the global query scope is only applied to queries made through the model. If you write database queries using the <code>IlluminateSupportFacadesDB</code> appearance, the global query scope is not applied. </p> <p>For example, suppose you wrote this query and you want it to crawl only articles belonging to the team of logged in users: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->published() ->get(); $unpublishedPosts = Article::query() ->notPublished() ->get();</code></pre> <p> In the above query, even if the global query scope of <code>AppModelsArticle</code> is defined on the <code>AppModelsScopesTeamScope</code> model, the scope will not be applied. Therefore, you need to make sure that constraints are applied manually in the database query. </p> <h1 id="Test-local-query-scope">Test local query scope</h1> <hr> <p> Now that we have learned how to create and use query scopes, we will look at how to write tests for them. </p> <p>There are several ways to test the scope of a query, and the method you choose may depend on your personal preference or the content of the scope you are writing. For example, you might want to write more unit-style tests for the scope. Alternatively, you might want to write more integration style tests that test scope in contexts like controllers. </p> <p> Personally, I like to mix the two so I can be sure that the scope is adding the correct constraints and that the scope is actually being used in the query. </p> <p>Let's start with the previous examples <code>published</code> and <code>notPublished</code> ranges and write some tests for them. We will need to write two different tests (one for each range): </p> <ul> <li>A test check<code>published</code> range returns only published articles. </li> <li>A test check<code>notPublished</code> range returns only unpublished articles. </li> </ul> <p>Let's look at these tests and then discuss what's being done: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; $publishedPosts = Article::query() ->where('published_at', '<', now()) ->get();</code></pre> <p> We can see in the above test file, we first create some data in the <code>setUp</code> method. We have created two published articles, one unscheduled article and one arranged article. </p> <p> Then there is a test (<code>only_published_articles_are_returned</code>) that checks the <code>published</code> range to return only published articles. There is also a test (<code>only_not_published_articles_are_returned</code>) that checks the <code>notPublished</code> range to return only unpublished articles. </p> <p> By doing this, we can now be sure that our query scope is applying constraints as expected. </p> <h1 id="Test-range-in-controller">Test range in controller</h1> <hr> <p>As we mentioned, another way to test the scope of a query is to test them in the context used in the controller. While the isolation testing of scopes can help assert that scope is adding the correct constraints to the query, it doesn't actually test whether the scope is used in the application as expected. For example, you might forget to add a <code>published</code> range to a query in the controller method. </p> <p> These types of errors can be captured by writing tests that asserts that return correct data when using scopes in controller methods. </p> <p> Let's take the example of having a multi-tenant blog application and write a test for the controller method that lists the articles. Let's assume we have a very simple controller method, as follows: </p> <pre class="brush:php;toolbar:false"><code>use App\Models\Article; use Illuminate\Contracts\Database\Eloquent\Builder; $unpublishedPosts = Article::query() ->where(function (Builder $query): void { $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }) ->get();</code></pre> <p>We assume that the <code>AppModelsArticle</code> model has applied our <code>AppModelsScopesTeamScope</code>. </p> <p>We will assert that we only return articles that belong to the user team. The test case might look like this: </p> <pre class="brush:php;toolbar:false"><code>declare(strict_types=1); namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; final class Article extends Model { public function scopePublished(Builder $query): Builder { return $query->where('published_at', '<', now()); } public function scopeNotPublished(Builder $query): Builder { return $query->where(function (Builder $query): Builder { return $query->whereNull('published_at') ->orWhere('published_at', '>', now()); }); } // ... }</code></pre> <p>In the above test, we are creating two teams. Then, we create a user belonging to Team One. We created 3 articles for Team 1 and 2 articles for Team 2. We then act as users and make a request to the controller method that lists the articles. The controller method should return only 3 articles belonging to Team One, so we assert that only those articles are returned by comparing the article IDs. </p> <p> This means we can be sure that the global query scope is being used as expected in the controller method. </p> <h1 id="Conclusion">Conclusion</h1> <hr> <p>In this article, we have learned about local query scope and global query scope. We learned the differences between them, how to create and use them, and how to write tests for them. </p> <p>Hope you should now be able to use query scopes confidently in your Laravel application. </p>

The above is the detailed content of Learn to master Query Scopes in Laravel. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

What are some best practices for versioning a PHP-based API? What are some best practices for versioning a PHP-based API? Jun 14, 2025 am 12:27 AM

ToversionaPHP-basedAPIeffectively,useURL-basedversioningforclarityandeaseofrouting,separateversionedcodetoavoidconflicts,deprecateoldversionswithclearcommunication,andconsidercustomheadersonlywhennecessary.StartbyplacingtheversionintheURL(e.g.,/api/v

How do I implement authentication and authorization in PHP? How do I implement authentication and authorization in PHP? Jun 20, 2025 am 01:03 AM

TosecurelyhandleauthenticationandauthorizationinPHP,followthesesteps:1.Alwayshashpasswordswithpassword_hash()andverifyusingpassword_verify(),usepreparedstatementstopreventSQLinjection,andstoreuserdatain$_SESSIONafterlogin.2.Implementrole-basedaccessc

What are weak references (WeakMap) in PHP, and when might they be useful? What are weak references (WeakMap) in PHP, and when might they be useful? Jun 14, 2025 am 12:25 AM

PHPdoesnothaveabuilt-inWeakMapbutoffersWeakReferenceforsimilarfunctionality.1.WeakReferenceallowsholdingreferenceswithoutpreventinggarbagecollection.2.Itisusefulforcaching,eventlisteners,andmetadatawithoutaffectingobjectlifecycles.3.YoucansimulateaWe

What are the differences between procedural and object-oriented programming paradigms in PHP? What are the differences between procedural and object-oriented programming paradigms in PHP? Jun 14, 2025 am 12:25 AM

Proceduralandobject-orientedprogramming(OOP)inPHPdiffersignificantlyinstructure,reusability,anddatahandling.1.Proceduralprogrammingusesfunctionsorganizedsequentially,suitableforsmallscripts.2.OOPorganizescodeintoclassesandobjects,modelingreal-worlden

How can you handle file uploads securely in PHP? How can you handle file uploads securely in PHP? Jun 19, 2025 am 01:05 AM

To safely handle file uploads in PHP, the core is to verify file types, rename files, and restrict permissions. 1. Use finfo_file() to check the real MIME type, and only specific types such as image/jpeg are allowed; 2. Use uniqid() to generate random file names and store them in non-Web root directory; 3. Limit file size through php.ini and HTML forms, and set directory permissions to 0755; 4. Use ClamAV to scan malware to enhance security. These steps effectively prevent security vulnerabilities and ensure that the file upload process is safe and reliable.

What are the differences between == (loose comparison) and === (strict comparison) in PHP? What are the differences between == (loose comparison) and === (strict comparison) in PHP? Jun 19, 2025 am 01:07 AM

In PHP, the main difference between == and == is the strictness of type checking. ==Type conversion will be performed before comparison, for example, 5=="5" returns true, and ===Request that the value and type are the same before true will be returned, for example, 5==="5" returns false. In usage scenarios, === is more secure and should be used first, and == is only used when type conversion is required.

How can you interact with NoSQL databases (e.g., MongoDB, Redis) from PHP? How can you interact with NoSQL databases (e.g., MongoDB, Redis) from PHP? Jun 19, 2025 am 01:07 AM

Yes, PHP can interact with NoSQL databases like MongoDB and Redis through specific extensions or libraries. First, use the MongoDBPHP driver (installed through PECL or Composer) to create client instances and operate databases and collections, supporting insertion, query, aggregation and other operations; second, use the Predis library or phpredis extension to connect to Redis, perform key-value settings and acquisitions, and recommend phpredis for high-performance scenarios, while Predis is convenient for rapid deployment; both are suitable for production environments and are well-documented.

How do I perform arithmetic operations in PHP ( , -, *, /, %)? How do I perform arithmetic operations in PHP ( , -, *, /, %)? Jun 19, 2025 pm 05:13 PM

The methods of using basic mathematical operations in PHP are as follows: 1. Addition signs support integers and floating-point numbers, and can also be used for variables. String numbers will be automatically converted but not recommended to dependencies; 2. Subtraction signs use - signs, variables are the same, and type conversion is also applicable; 3. Multiplication signs use * signs, which are suitable for numbers and similar strings; 4. Division uses / signs, which need to avoid dividing by zero, and note that the result may be floating-point numbers; 5. Taking the modulus signs can be used to judge odd and even numbers, and when processing negative numbers, the remainder signs are consistent with the dividend. The key to using these operators correctly is to ensure that the data types are clear and the boundary situation is handled well.

See all articles