Repository: biiiiiigmonster/hasin
Branch: master
Commit: 931dc68018e2
Files: 67
Total size: 88.0 KB
Directory structure:
gitextract_znsrf2cn/
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── config.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── dependabot-auto-merge.yml
│ ├── fix-php-code-style-issues.yml
│ └── run-tests.yml
├── .gitignore
├── .scrutinizer.yml
├── CHANGELOG.md
├── LICENSE
├── README-CN.md
├── README.md
├── _laravel_ide_helper.php
├── composer.json
├── database/
│ ├── factories/
│ │ ├── CommentFactory.php
│ │ ├── CountryFactory.php
│ │ ├── HistoryFactory.php
│ │ ├── ImageFactory.php
│ │ ├── PhoneFactory.php
│ │ ├── PostFactory.php
│ │ ├── RoleFactory.php
│ │ ├── SupplierFactory.php
│ │ ├── TagFactory.php
│ │ ├── UserFactory.php
│ │ └── VideoFactory.php
│ └── migrations/
│ └── create_hasin_test_table.php
├── phpunit.xml.dist
├── pint.json
├── src/
│ ├── Database/
│ │ └── Eloquent/
│ │ ├── BuilderMixin.php
│ │ └── RelationMixin.php
│ └── HasinServiceProvider.php
└── tests/
├── Features/
│ ├── DoesntHaveInTest.php
│ ├── DoesntHaveMorphInTest.php
│ ├── HasInTest.php
│ ├── HasMorphInTest.php
│ ├── OrDoesntHaveInTest.php
│ ├── OrDoesntHaveMorphInTest.php
│ ├── OrHasInTest.php
│ ├── OrHasMorphInTest.php
│ ├── OrWhereDoesntHaveInTest.php
│ ├── OrWhereDoesntHaveMorphInTest.php
│ ├── OrWhereHasInTest.php
│ ├── OrWhereHasMorphInTest.php
│ ├── OrWhereMorphRelationInTest.php
│ ├── OrWhereRelationInTest.php
│ ├── WhereDoesntHaveInTest.php
│ ├── WhereDoesntHaveMorphInTest.php
│ ├── WhereHasInTest.php
│ ├── WhereHasMorphInTest.php
│ ├── WhereMorphRelationInTest.php
│ ├── WhereRelationInTest.php
│ └── WithWhereHasInTest.php
├── Models/
│ ├── Comment.php
│ ├── Country.php
│ ├── History.php
│ ├── Image.php
│ ├── Phone.php
│ ├── Post.php
│ ├── Role.php
│ ├── RoleUser.php
│ ├── Supplier.php
│ ├── Tag.php
│ ├── Taggable.php
│ ├── User.php
│ └── Video.php
├── Pest.php
└── TestCase.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Path-based git attributes
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
# Ignore all test and documentation with "export-ignore".
/.github export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml export-ignore
/phpunit.xml.dist export-ignore
/art export-ignore
/database export-ignore
/docs export-ignore
/tests export-ignore
/.editorconfig export-ignore
/.php_cs.dist.php export-ignore
/psalm.xml export-ignore
/psalm.xml.dist export-ignore
/pint.json export-ignore
/testbench.yaml export-ignore
/UPGRADING.md export-ignore
/phpstan.neon.dist export-ignore
/phpstan-baseline.neon export-ignore
/.scrutinizer.yml export-ignore
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/biiiiiigmonster/hasin/discussions/new?category=q-a
about: Ask the community for help
- name: Request a feature
url: https://github.com/biiiiiigmonster/hasin/discussions/new?category=ideas
about: Share ideas for new features
- name: Report a security issue
url: https://github.com/biiiiiigmonster/hasin/security/policy
about: Learn how to notify us for sensitive bugs
- name: Report a bug
url: https://github.com/biiiiiigmonster/hasin/issues/new
about: Report a reproducable bug
================================================
FILE: .github/dependabot.yml
================================================
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
================================================
FILE: .github/workflows/dependabot-auto-merge.yml
================================================
name: dependabot-auto-merge
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2.5.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Auto-merge Dependabot PRs for semver-minor updates
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Auto-merge Dependabot PRs for semver-patch updates
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
================================================
FILE: .github/workflows/fix-php-code-style-issues.yml
================================================
name: Fix PHP code style issues
on: [push]
jobs:
php-code-styling:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ github.head_ref }}
- name: Fix PHP code style issues
uses: aglipanci/laravel-pint-action@2.6
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: Fix styling
================================================
FILE: .github/workflows/run-tests.yml
================================================
name: run-tests
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
test:
runs-on: ${{ matrix.os }}
services:
mysql:
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
ports:
- 33306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: [8.2, 8.3, 8.4]
laravel: ['12.*', '13.*']
stability: [prefer-lowest, prefer-stable]
include:
- laravel: 12.*
testbench: 10.*
- laravel: 13.*
testbench: 11.*
exclude:
- laravel: 13.*
php: 8.2
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
coverage: xdebug
- name: Setup problem matchers
run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Install dependencies
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
- name: Execute tests
run: vendor/bin/pest
env:
DB_PORT: 33306
DB_PASSWORD: root
================================================
FILE: .gitignore
================================================
.idea
.phpunit.result.cache
build
composer.lock
coverage
docs
phpunit.xml
testbench.yaml
vendor
node_modules
.phpunit.cache
================================================
FILE: .scrutinizer.yml
================================================
checks:
php:
code_rating: true
remove_extra_empty_lines: true
remove_php_closing_tag: true
remove_trailing_whitespace: true
fix_use_statements:
remove_unused: true
preserve_multiple: false
preserve_blanklines: true
order_alphabetically: true
fix_linefeed: true
fix_line_ending: true
fix_identation_4spaces: true
build:
image: default-bionic
environment:
php:
version: 8.2
tests:
override:
- command: './vendor/bin/pest'
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to `hasin` will be documented in this file.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) Yunfeng Lu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README-CN.md
================================================
[English](./README.md) | 中文
<div align="center">
# LARAVEL HASIN
[](https://packagist.org/packages/biiiiiigmonster/hasin)
[](https://github.com/biiiiiigmonster/hasin/actions?query=workflow%3Arun-tests+branch%3Amaster)
[](https://github.com/biiiiiigmonster/hasin/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amaster)
[](https://coveralls.io/github/biiiiiigmonster/hasin?branch=master)
[](https://scrutinizer-ci.com/g/biiiiiigmonster/hasin/)
[](https://packagist.org/packages/biiiiiigmonster/hasin)
</div>
`hasin`是一个基于`where in`语法实现的`Laravel ORM`关联关系查询的扩展包,部分业务场景下可以替代`Laravel ORM`中基于`where exists`语法实现的`has`,以获取更高的性能。
## 安装
| Laravel 版本 | 安装命令 |
|-----------------|-----------------------------------------------------|
| Laravel 12 | ``` composer require biiiiiigmonster/hasin:^5.0 ``` |
| Laravel 11 | ``` composer require biiiiiigmonster/hasin:^4.0 ``` |
| Laravel 10 | ``` composer require biiiiiigmonster/hasin:^3.0 ``` |
| Laravel 9 | ``` composer require biiiiiigmonster/hasin:^2.0 ``` |
| Laravel 5.5 ~ 8 | ``` composer require biiiiiigmonster/hasin:^1.0 ``` |
## 简介
`Laravel ORM`的关联关系非常强大,基于关联关系的查询`has`也给我们提供了诸多灵活的调用方式,然而某些情形下,`has`使用了**where exists**语法实现
例如:
```php
// User hasMany Post
User::has('posts')->get();
```
#### `select * from users where exists (select * from posts where users.id = posts.user_id)`
> exists是对外表做loop循环,每次loop循环再对内表(子查询)进行查询,那么因为对内表的查询使用的索引(内表效率高,故可用大表),而外表有多大都需要遍历,不可避免(尽量用小表),故内表大的使用exists,可加快效率。
当**User表**数据量较大的时候,就会出现性能问题,那么这时候用**where in**语法将会极大的提高性能
#### `select * from users where users.id in (select posts.user_id from posts)`
> in是把外表和内表做hash连接,先查询内表,再把内表结果与外表匹配,对外表使用索引(外表效率高,可用大表),而内表多大都需要查询,不可避免,故外表大的使用in,可加快效率。
因此在代码中使用`has(hasMorph)`或者`hasIn(hasMorphIn)`应由**数据体量**来决定……
```php
/**
* SQL:
*
* select * from `users`
* where exists
* (
* select * from `posts`
* where `users`.`id` = `posts`.`user_id`
* )
* limit 10 offset 0
*/
$users = User::has('posts')->paginate(10);
/**
* SQL:
*
* select * from `users`
* where `users`.`id` in
* (
* select `posts`.`user_id` from `posts`
* )
* limit 10 offset 0
*/
$users = User::hasIn('posts')->paginate(10);
```
## 使用
此扩展方法`hasIn(hasMorphIn)`支持`Laravel ORM`中的所有关联关系,入参调用及内部实现流程与框架的`has(hasMorph)`完全一致。
> hasIn
```php
// hasIn
User::hasIn('posts')->get();
// orHasIn
User::where('age', '>', 18)->orHasIn('posts')->get();
// doesntHaveIn
User::doesntHaveIn('posts')->get();
// orDoesntHaveIn
User::where('age', '>', 18)->orDoesntHaveIn('posts')->get();
```
> whereHasIn
```php
// whereHasIn
User::whereHasIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
// orWhereHasIn
User::where('age', '>', 18)->orWhereHasIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
// whereDoesntHaveIn
User::whereDoesntHaveIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
// orWhereDoesntHaveIn
User::where('age', '>', 18)->orWhereDoesntHaveIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
```
> hasMorphIn
```php
Image::hasMorphIn('imageable', [Post::class, Comment::class])->get();
```
### 嵌套关联
```php
User::hasIn('posts.comments')->get();
```
## 测试
```bash
composer test
```
## 联系交流
wx:biiiiiigmonster(备注:hasin)
## 协议
[MIT 协议](LICENSE)
================================================
FILE: README.md
================================================
English | [中文](./README-CN.md)
<div align="center">
# LARAVEL HASIN
[](https://packagist.org/packages/biiiiiigmonster/hasin)
[](https://github.com/biiiiiigmonster/hasin/actions?query=workflow%3Arun-tests+branch%3Amaster)
[](https://github.com/biiiiiigmonster/hasin/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amaster)
[](https://coveralls.io/github/biiiiiigmonster/hasin?branch=master)
[](https://scrutinizer-ci.com/g/biiiiiigmonster/hasin/)
[](https://packagist.org/packages/biiiiiigmonster/hasin)
</div>
The `hasin` is composer package based on `where in` syntax to query the relationship of `laravel ORM`, which can replace `has` based on `where exists` syntax in some business scenarios to obtain higher performance.
# Installation
| Laravel Version | Install command |
|-----------------|-----------------------------------------------------|
| Laravel 12 | ``` composer require biiiiiigmonster/hasin:^5.0 ``` |
| Laravel 11 | ``` composer require biiiiiigmonster/hasin:^4.0 ``` |
| Laravel 10 | ``` composer require biiiiiigmonster/hasin:^3.0 ``` |
| Laravel 9 | ``` composer require biiiiiigmonster/hasin:^2.0 ``` |
| Laravel 5.5 ~ 8 | ``` composer require biiiiiigmonster/hasin:^1.0 ``` |
# Introductions
The relationship of `laravel ORM` is very powerful, and the query `has` based on the relationship also provides us with many flexible calling methods. However, in some cases, `has` is implemented with **where exists** syntax.
For example:
```php
// User hasMany Post
User::has('posts')->get();
```
#### `select * from users where exists (select * from posts where users.id = posts.user_id)`
> 'exists' is a loop to the external table, and then queries the internal table (subQuery) every time. Because the index used for the query of the internal table (the internal table is efficient, so it can be used as a large table), and how much of the external table needs to be traversed, it is inevitable (try to use a small table), so the use of exists for the large internal table can speed up the efficiency.
When the **User** table has a large amount of data, there will be performance problems, so the **where in** syntax will greatly improve the performance.
#### `select * from users where users.id in (select posts.user_id from posts)`
> 'in' is to hash connect the appearance and inner table, first query the inner table, then match the result of the inner table with the appearance, and use the index for the outer table (the appearance is efficient, and large tables can be used). Most of the inner tables need to be queried, which is inevitable. Therefore, using 'in' with large appearance can speed up the efficiency.
Therefore, the use of `has(hasMorph)` or `hasIn(hasMorphIn)` in code should be determined by **data size**
```php
/**
* SQL:
*
* select * from `users`
* where exists
* (
* select * from `posts`
* where `users`.`id` = `posts`.`user_id`
* )
* limit 10 offset 0
*/
$users = User::has('posts')->paginate(10);
/**
* SQL:
*
* select * from `users`
* where `users`.`id` in
* (
* select `posts`.`user_id` from `posts`
* )
* limit 10 offset 0
*/
$users = User::hasIn('posts')->paginate(10);
```
# Usage example
`hasIn(hasMorphIn)` supports all `Relations` in `laravel ORM`. The call mode and internal implementation are completely consistent with `has(hasMorph)` of the framework.
> hasIn
```php
// hasIn
User::hasIn('posts')->get();
// orHasIn
User::where('age', '>', 18)->orHasIn('posts')->get();
// doesntHaveIn
User::doesntHaveIn('posts')->get();
// orDoesntHaveIn
User::where('age', '>', 18)->orDoesntHaveIn('posts')->get();
```
> whereHasIn
```php
// whereHasIn
User::whereHasIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
// orWhereHasIn
User::where('age', '>', 18)->orWhereHasIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
// whereDoesntHaveIn
User::whereDoesntHaveIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
// orWhereDoesntHaveIn
User::where('age', '>', 18)->orWhereDoesntHaveIn('posts', function ($query) {
$query->where('votes', '>', 10);
})->get();
```
> hasMorphIn
```php
Image::hasMorphIn('imageable', [Post::class, Comment::class])->get();
```
### Nested Relation
```php
User::hasIn('posts.comments')->get();
```
# Testing
```bash
composer test
```
>**Tips**: before testing, you need to configure your database connection in the `phpunit.xml.dist`.
# License
[MIT](./LICENSE)
================================================
FILE: _laravel_ide_helper.php
================================================
<?php
namespace Illuminate\Database\Eloquent
{
use Closure;
if (false) {
class Builder
{
/**
* Add a relationship count / whereIn condition to the query.
*
* @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation
* @param string $operator
* @param int $count
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @throws \RuntimeException
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function hasIn($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query with an "or".
*
* @param string $relation
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orHasIn($relation, $operator = '>=', $count = 1)
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query.
*
* @param string $relation
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function doesntHaveIn($relation, $boolean = 'and', Closure $callback = null)
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query with an "or".
*
* @return Closure
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orDoesntHaveIn()
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query with where clauses.
*
* @param string $relation
* @param \Closure|null $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function whereHasIn($relation, Closure $callback = null, $operator = '>=', $count = 1)
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query with where clauses and an "or".
*
* @param string $relation
* @param \Closure|null $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orWhereHasIn($relation, Closure $callback = null, $operator = '>=', $count = 1)
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query with where clauses.
*
* @param string $relation
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function whereDoesntHaveIn($relation, Closure $callback = null)
{
return $this;
}
/**
* Add a relationship count / whereIn condition to the query with where clauses and an "or".
*
* @param string $relation
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orWhereDoesntHaveIn($relation, Closure $callback = null)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query.
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param string $operator
* @param int $count
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function hasMorphIn($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with an "or".
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orHasMorphIn($relation, $types, $operator = '>=', $count = 1)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query.
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param string $boolean
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function doesntHaveMorphIn($relation, $types, $boolean = 'and', Closure $callback = null)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with an "or".
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orDoesntHaveMorphIn($relation, $types)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses.
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*
* @return Closure
*/
public function whereHasMorphIn()
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses.
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param \Closure|null $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orWhereHasMorphIn($relation, $types, Closure $callback = null, $operator = '>=', $count = 1)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses.
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function whereDoesntHaveMorphIn($relation, $types, Closure $callback = null)
{
return $this;
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses and an "or".
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*
* @see \BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin
*/
public function orWhereDoesntHaveMorphIn($relation, $types, Closure $callback = null)
{
return $this;
}
/**
* Add a basic where clause to a relationship query.
*
* @param string $relation
* @param \Closure|string|array|\Illuminate\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function whereRelationIn($relation, $column, $operator = null, $value = null)
{
return $this;
}
/**
* Add an "or where" clause to a relationship query.
*
* @param string $relation
* @param \Closure|string|array|\Illuminate\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhereRelationIn($relation, $column, $operator = null, $value = null)
{
return $this;
}
/**
* Add a polymorphic relationship condition to the query with a where clause.
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param \Closure|string|array|\Illuminate\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function whereMorphRelationIn($relation, $types, $column, $operator = null, $value = null)
{
return $this;
}
/**
* Add a polymorphic relationship condition to the query with an "or where" clause.
*
* @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation
* @param string|array $types
* @param \Closure|string|array|\Illuminate\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhereMorphRelationIn($relation, $types, $column, $operator = null, $value = null)
{
return $this;
}
}
}
}
================================================
FILE: composer.json
================================================
{
"name": "biiiiiigmonster/hasin",
"description": "Laravel framework relation has in implement",
"type": "library",
"keywords": [
"laravel",
"orm",
"relation",
"whereHas"
],
"license": "MIT",
"authors": [
{
"name": "YunFeng Lu",
"email": "603707288@qq.com"
}
],
"require": {
"laravel/framework": "^12|^13.0"
},
"require-dev": {
"laravel/pint": "^1.1",
"orchestra/testbench": "^10.0|^11.0",
"pestphp/pest": "^3.0|^4.4",
"pestphp/pest-plugin-laravel": "^3.0|^4.1"
},
"autoload": {
"psr-4": {
"BiiiiiigMonster\\Hasin\\": "src",
"BiiiiiigMonster\\Hasin\\Database\\Factories\\": "database/factories"
}
},
"autoload-dev": {
"psr-4": {
"BiiiiiigMonster\\Hasin\\Tests\\": "tests"
}
},
"extra": {
"laravel": {
"providers": [
"BiiiiiigMonster\\Hasin\\HasinServiceProvider"
]
}
},
"scripts": {
"test": "vendor/bin/pest",
"format": "vendor/bin/pint"
},
"config": {
"sort-packages": true,
"allow-plugins": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
================================================
FILE: database/factories/CommentFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use Illuminate\Database\Eloquent\Factories\Factory;
class CommentFactory extends Factory
{
protected $model = Comment::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'content' => $this->faker->sentence,
'status' => $this->faker->numberBetween(0, 9),
];
}
}
================================================
FILE: database/factories/CountryFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Country;
use Illuminate\Database\Eloquent\Factories\Factory;
class CountryFactory extends Factory
{
protected $model = Country::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->country
];
}
}
================================================
FILE: database/factories/HistoryFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\History;
use Illuminate\Database\Eloquent\Factories\Factory;
class HistoryFactory extends Factory
{
protected $model = History::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'content' => $this->faker->address
];
}
}
================================================
FILE: database/factories/ImageFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Image;
use Illuminate\Database\Eloquent\Factories\Factory;
class ImageFactory extends Factory
{
protected $model = Image::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'url' => $this->faker->url
];
}
}
================================================
FILE: database/factories/PhoneFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Phone;
use Illuminate\Database\Eloquent\Factories\Factory;
class PhoneFactory extends Factory
{
protected $model = Phone::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'phone_number' => $this->faker->phoneNumber
];
}
}
================================================
FILE: database/factories/PostFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
protected $model = Post::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'title' => $this->faker->title,
'votes' => $this->faker->numberBetween(0, 100),
];
}
}
================================================
FILE: database/factories/RoleFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Role;
use Illuminate\Database\Eloquent\Factories\Factory;
class RoleFactory extends Factory
{
protected $model = Role::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name
];
}
}
================================================
FILE: database/factories/SupplierFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Supplier;
use Illuminate\Database\Eloquent\Factories\Factory;
class SupplierFactory extends Factory
{
protected $model = Supplier::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name
];
}
}
================================================
FILE: database/factories/TagFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Tag;
use Illuminate\Database\Eloquent\Factories\Factory;
class TagFactory extends Factory
{
protected $model = Tag::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name
];
}
}
================================================
FILE: database/factories/UserFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
protected $model = User::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'username' => $this->faker->userName,
'age' => $this->faker->numberBetween(10, 30),
];
}
}
================================================
FILE: database/factories/VideoFactory.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Factories;
use BiiiiiigMonster\Hasin\Tests\Models\Video;
use Illuminate\Database\Eloquent\Factories\Factory;
class VideoFactory extends Factory
{
protected $model = Video::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name
];
}
}
================================================
FILE: database/migrations/create_hasin_test_table.php
================================================
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('histories', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->default(0);
$table->string('content');
$table->timestamps();
});
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->morphs('commentable');
$table->string('content');
$table->tinyInteger('status')->default(0);
$table->timestamps();
});
Schema::create('countries', function (Blueprint $table) {
$table->id();
$table->string('name')->default('');
$table->timestamps();
});
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('url')->default('');
$table->morphs('imageable');
$table->timestamps();
});
Schema::create('phones', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->default(0);
$table->string('phone_number')->default('');
$table->timestamps();
});
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('role_user', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->default(0);
$table->bigInteger('role_id')->default(0);
$table->timestamps();
});
Schema::create('suppliers', function (Blueprint $table) {
$table->id();
$table->string('name')->default('');
$table->timestamps();
});
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name')->default('');
$table->timestamps();
});
Schema::create('taggables', function (Blueprint $table) {
$table->id();
$table->bigInteger('tag_id')->default(0);
$table->morphs('taggable');
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('username')->default('');
$table->tinyInteger('age')->default(0);
$table->bigInteger('country_id')->default(0);
$table->bigInteger('supplier_id')->default(0);
$table->timestamps();
});
Schema::create('videos', function (Blueprint $table) {
$table->id();
$table->string('name')->default('');
$table->timestamps();
});
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->default(0);
$table->string('title')->default('');
$table->integer('votes')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('histories');
Schema::dropIfExists('comments');
Schema::dropIfExists('countries');
Schema::dropIfExists('images');
Schema::dropIfExists('phones');
Schema::dropIfExists('roles');
Schema::dropIfExists('role_user');
Schema::dropIfExists('suppliers');
Schema::dropIfExists('tags');
Schema::dropIfExists('taggables');
Schema::dropIfExists('users');
Schema::dropIfExists('videos');
Schema::dropIfExists('posts');
}
};
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
backupGlobals="false"
bootstrap="vendor/autoload.php"
colors="true"
processIsolation="false"
stopOnFailure="false"
executionOrder="random"
failOnWarning="true"
failOnRisky="true"
failOnEmptyTestSuite="true"
beStrictAboutOutputDuringTests="true"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
>
<testsuites>
<testsuite name="Hasin Test Suite">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<php>
<env name="DB_HOST" value="127.0.0.1"/>
<env name="DB_PORT" value="3306"/>
<env name="DB_DATABASE" value="laravel"/>
<env name="DB_USERNAME" value="root"/>
<env name="DB_PASSWORD" value="root"/>
</php>
</phpunit>
================================================
FILE: pint.json
================================================
{
"preset": "psr12",
"exclude": [
"build"
]
}
================================================
FILE: src/Database/Eloquent/BuilderMixin.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Eloquent;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Str;
class BuilderMixin
{
/**
* Add a relationship count / whereIn condition to the query.
*
* @return Closure
*/
public function hasIn(): Closure
{
return function ($relation, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null): Builder {
/** @var Builder $this */
if (is_string($relation)) {
if (str_contains($relation, '.')) {
return $this->hasInNested($relation, $operator, $count, $boolean, $callback);
}
$relation = $this->getRelationWithoutConstraints($relation);
}
if ($relation instanceof MorphTo) {
return $this->hasMorphIn($relation, ['*'], $operator, $count, $boolean, $callback);
}
// If we only need to check for the existence of the relation, then we can optimize
// the subquery to only run a "where in" clause instead of this full "count"
// clause. This will make these queries run much faster compared with a count.
$method = $this->canUseExistsForExistenceCheck($operator, $count)
? 'getRelationExistenceInQuery'
: 'getRelationExistenceCountQuery';
$hasInQuery = $relation->{$method}(
$relation->getRelated()->newQueryWithoutRelationships(),
$this
);
// Next we will call any given callback as an "anonymous" scope so they can get the
// proper logical grouping of the where clauses if needed by this Eloquent query
// builder. Then, we will be ready to finalize and return this query instance.
if ($callback) {
$hasInQuery->callScope($callback);
}
return $this->addHasInWhere(
$hasInQuery,
$relation,
$operator,
$count,
$boolean
);
};
}
/**
* Add nested relationship count / whereIn conditions to the query.
*
* Sets up recursive call to whereHas until we finish the nested relation.
*
* @return Closure
*/
protected function hasInNested(): Closure
{
return function ($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null): Builder {
/** @var Builder $this */
$relations = explode('.', $relations);
$doesntHave = $operator === '<' && $count === 1;
if ($doesntHave) {
$operator = '>=';
$count = 1;
}
$closure = function ($q) use (&$closure, &$relations, $operator, $count, $callback) {
// In order to nest "hasIn", we need to add count relation constraints on the
// callback Closure. We'll do this by simply passing the Closure its own
// reference to itself so it calls itself recursively on each segment.
count($relations) > 1
? $q->whereHasIn(array_shift($relations), $closure)
: $q->hasIn(array_shift($relations), $operator, $count, 'and', $callback);
};
return $this->hasIn(array_shift($relations), $doesntHave ? '<' : '>=', 1, $boolean, $closure);
};
}
/**
* Add a relationship count / whereIn condition to the query with an "or".
*
* @return Closure
*/
public function orHasIn(): Closure
{
return function ($relation, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->hasIn($relation, $operator, $count, 'or');
};
}
/**
* Add a relationship count / whereIn condition to the query.
*
* @return Closure
*/
public function doesntHaveIn(): Closure
{
return function ($relation, $boolean = 'and', ?Closure $callback = null): Builder {
/** @var Builder $this */
return $this->hasIn($relation, '<', 1, $boolean, $callback);
};
}
/**
* Add a relationship count / whereIn condition to the query with an "or".
*
* @return Closure
*/
public function orDoesntHaveIn(): Closure
{
return function ($relation): Builder {
/** @var Builder $this */
return $this->doesntHaveIn($relation, 'or');
};
}
/**
* Add a relationship count / whereIn condition to the query with where clauses.
*
* @return Closure
*/
public function whereHasIn(): Closure
{
return function ($relation, ?Closure $callback = null, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->hasIn($relation, $operator, $count, 'and', $callback);
};
}
/**
* Add a relationship count / exists condition to the query with whereIn clauses.
*
* Also load the relationship with same condition.
*
* @return Closure
*/
public function withWhereHasIn(): Closure
{
return function ($relation, ?Closure $callback = null, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->whereHasIn(Str::before($relation, ':'), $callback, $operator, $count)
->with($callback ? [$relation => fn ($query) => $callback($query)] : $relation);
};
}
/**
* Add a relationship count / whereIn condition to the query with where clauses and an "or".
*
* @return Closure
*/
public function orWhereHasIn(): Closure
{
return function ($relation, ?Closure $callback = null, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->hasIn($relation, $operator, $count, 'or', $callback);
};
}
/**
* Add a relationship count / whereIn condition to the query with where clauses.
*
* @return Closure
*/
public function whereDoesntHaveIn(): Closure
{
return function ($relation, ?Closure $callback = null): Builder {
/** @var Builder $this */
return $this->doesntHaveIn($relation, 'and', $callback);
};
}
/**
* Add a relationship count / whereIn condition to the query with where clauses and an "or".
*
* @return Closure
*/
public function orWhereDoesntHaveIn(): Closure
{
return function ($relation, ?Closure $callback = null): Builder {
/** @var Builder $this */
return $this->doesntHaveIn($relation, 'or', $callback);
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query.
*
* @return Closure
*/
public function hasMorphIn(): Closure
{
return function ($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null): Builder {
/** @var Builder $this */
if (is_string($relation)) {
$relation = $this->getRelationWithoutConstraints($relation);
}
$types = (array) $types;
if ($types === ['*']) {
$types = $this->model->newModelQuery()->distinct()->pluck($relation->getMorphType())->filter()->all();
}
foreach ($types as &$type) {
$type = Relation::getMorphedModel($type) ?? $type;
}
return $this->where(function ($query) use ($relation, $callback, $operator, $count, $types) {
foreach ($types as $type) {
$query->orWhere(function ($query) use ($relation, $callback, $operator, $count, $type) {
$belongsTo = $this->getBelongsToRelation($relation, $type);
if ($callback) {
$callback = function ($query) use ($callback, $type) {
return $callback($query, $type);
};
}
$query->where($this->qualifyColumn($relation->getMorphType()), '=', (new $type())->getMorphClass())
->whereHasIn($belongsTo, $callback, $operator, $count);
});
}
}, null, null, $boolean);
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with an "or".
*
* @return Closure
*/
public function orHasMorphIn(): Closure
{
return function ($relation, $types, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->hasMorphIn($relation, $types, $operator, $count, 'or');
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query.
*
* @return Closure
*/
public function doesntHaveMorphIn(): Closure
{
return function ($relation, $types, $boolean = 'and', ?Closure $callback = null): Builder {
/** @var Builder $this */
return $this->hasMorphIn($relation, $types, '<', 1, $boolean, $callback);
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with an "or".
*
* @return Closure
*/
public function orDoesntHaveMorphIn(): Closure
{
return function ($relation, $types): Builder {
/** @var Builder $this */
return $this->doesntHaveMorphIn($relation, $types, 'or');
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses.
*
* @return Closure
*/
public function whereHasMorphIn(): Closure
{
return function ($relation, $types, ?Closure $callback = null, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->hasMorphIn($relation, $types, $operator, $count, 'and', $callback);
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses and an "or".
*
* @return Closure
*/
public function orWhereHasMorphIn(): Closure
{
return function ($relation, $types, ?Closure $callback = null, $operator = '>=', $count = 1): Builder {
/** @var Builder $this */
return $this->hasMorphIn($relation, $types, $operator, $count, 'or', $callback);
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses.
*
* @return Closure
*/
public function whereDoesntHaveMorphIn(): Closure
{
return function ($relation, $types, ?Closure $callback = null): Builder {
/** @var Builder $this */
return $this->doesntHaveMorphIn($relation, $types, 'and', $callback);
};
}
/**
* Add a polymorphic relationship count / whereIn condition to the query with where clauses and an "or".
*
* @return Closure
*/
public function orWhereDoesntHaveMorphIn(): Closure
{
return function ($relation, $types, ?Closure $callback = null): Builder {
/** @var Builder $this */
return $this->doesntHaveMorphIn($relation, $types, 'or', $callback);
};
}
/**
* Add a basic where clause to a relationship query.
*
* @return Closure
*/
public function whereRelationIn(): Closure
{
return function ($relation, $column, $operator = null, $value = null): Builder {
return $this->whereHasIn($relation, function ($query) use ($column, $operator, $value) {
if ($column instanceof Closure) {
$column($query);
} else {
$query->where($column, $operator, $value);
}
});
};
}
/**
* Add an "or where" clause to a relationship query.
*
* @return Closure
*/
public function orWhereRelationIn(): Closure
{
return function ($relation, $column, $operator = null, $value = null): Builder {
return $this->orWhereHasIn($relation, function ($query) use ($column, $operator, $value) {
if ($column instanceof Closure) {
$column($query);
} else {
$query->where($column, $operator, $value);
}
});
};
}
/**
* Add a polymorphic relationship condition to the query with a where clause.
*
* @return Closure
*/
public function whereMorphRelationIn(): Closure
{
return function ($relation, $types, $column, $operator = null, $value = null): Builder {
return $this->whereHasMorphIn($relation, $types, function ($query) use ($column, $operator, $value) {
$query->where($column, $operator, $value);
});
};
}
/**
* Add a polymorphic relationship condition to the query with an "or where" clause.
*
* @return Closure
*/
public function orWhereMorphRelationIn(): Closure
{
return function ($relation, $types, $column, $operator = null, $value = null): Builder {
return $this->orWhereHasMorphIn($relation, $types, function ($query) use ($column, $operator, $value) {
$query->where($column, $operator, $value);
});
};
}
/**
* Add the "hasin" condition whereIn clause to the query.
*
* @return Closure
*/
protected function addHasInWhere(): Closure
{
return function (Builder $hasInQuery, Relation $relation, $operator, $count, $boolean): Builder {
/** @var Builder $this */
$hasInQuery->mergeConstraintsFrom($relation->getQuery());
return $this->canUseExistsForExistenceCheck($operator, $count)
? $this->whereIn($relation->getRelationWhereInKey(), $hasInQuery->toBase(), $boolean, $operator === '<' && $count === 1)
: $this->addWhereCountQuery($hasInQuery->toBase(), $operator, $count, $boolean);
};
}
}
================================================
FILE: src/Database/Eloquent/RelationMixin.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Database\Eloquent;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
use Illuminate\Database\Eloquent\Relations\HasOneOrManyThrough;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use LogicException;
class RelationMixin
{
public function getRelationExistenceInQuery(): Closure
{
return function (Builder $query, Builder $parentQuery, $columns = ['*']): Builder {
$relation = function (Builder $query, Builder $parentQuery, $columns = ['*']): Builder {
return $query->select($columns);
};
// basic builder
$belongsTo = function (Builder $query, Builder $parentQuery) use ($relation): Builder {
$columns = $query->qualifyColumn($this->ownerKey);
$relationQuery = $relation($query, $parentQuery, $columns);
if ($parentQuery->getQuery()->from == $query->getQuery()->from) {
$relationQuery->from(
$query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash()
);
$relationQuery->getModel()->setTable($hash);
}
return $relationQuery;
};
$belongsToMany = function (Builder $query, Builder $parentQuery) use ($relation): Builder {
$columns = $this->getExistenceCompareKey();
if ($parentQuery->getQuery()->from == $query->getQuery()->from) {
$query->select($columns);
$query->from($this->related->getTable().' as '.$hash = $this->getRelationCountHash());
$this->related->setTable($hash);
}
$this->performJoin($query);
return $relation($query, $parentQuery, $columns);
};
$hasOneOrMany = function (Builder $query, Builder $parentQuery) use ($relation): Builder {
$columns = $this->getExistenceCompareKey();
if ($query->getQuery()->from == $parentQuery->getQuery()->from) {
$query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
$query->getModel()->setTable($hash);
}
return $relation($query, $parentQuery, $columns);
};
$hasOneOrManyThrough = function (Builder $query, Builder $parentQuery) use ($relation): Builder {
$columns = $this->getQualifiedFirstKeyName();
if ($parentQuery->getQuery()->from === $query->getQuery()->from) {
$query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
$query->join($this->throughParent->getTable(), $this->getQualifiedParentKeyName(), '=', $hash.'.'.$this->secondKey);
if ($this->throughParentSoftDeletes()) {
$query->whereNull($this->throughParent->getQualifiedDeletedAtColumn());
}
$query->getModel()->setTable($hash);
return $relation($query, $parentQuery, $columns);
}
if ($parentQuery->getQuery()->from === $this->throughParent->getTable()) {
$table = $this->throughParent->getTable().' as '.$hash = $this->getRelationCountHash();
$query->join($table, $hash.'.'.$this->secondLocalKey, '=', $this->getQualifiedFarKeyName());
if ($this->throughParentSoftDeletes()) {
$query->whereNull($hash.'.'.$this->throughParent->getDeletedAtColumn());
}
return $relation($query, $parentQuery, $columns);
}
$this->performJoin($query);
return $relation($query, $parentQuery, $columns);
};
// extended builder
$hasOne = function (Builder $query, Builder $parentQuery) use ($hasOneOrMany): Builder {
if ($this->isOneOfMany()) {
$this->mergeOneOfManyJoinsTo($query);
}
return $hasOneOrMany($query, $parentQuery);
};
$morphOneOrMany = function (Builder $query, Builder $parentQuery) use ($hasOneOrMany): Builder {
return $hasOneOrMany($query, $parentQuery)->where(
$query->qualifyColumn($this->getMorphType()),
$this->morphClass
);
};
$morphOne = function (Builder $query, Builder $parentQuery) use ($morphOneOrMany): Builder {
if ($this->isOneOfMany()) {
$this->mergeOneOfManyJoinsTo($query);
}
return $morphOneOrMany($query, $parentQuery);
};
$morphToMany = function (Builder $query, Builder $parentQuery) use ($belongsToMany): Builder {
return $belongsToMany($query, $parentQuery)->where(
$this->qualifyPivotColumn($this->morphType),
$this->morphClass
);
};
return match (true) {
// extended relation
$this instanceof HasOne => $hasOne($query, $parentQuery),
$this instanceof MorphOne => $morphOne($query, $parentQuery),
$this instanceof MorphOneOrMany => $morphOneOrMany($query, $parentQuery),
$this instanceof MorphToMany => $morphToMany($query, $parentQuery),
// basic relation
$this instanceof BelongsTo => $belongsTo($query, $parentQuery),
$this instanceof BelongsToMany => $belongsToMany($query, $parentQuery),
$this instanceof HasOneOrMany => $hasOneOrMany($query, $parentQuery),
$this instanceof HasOneOrManyThrough => $hasOneOrManyThrough($query, $parentQuery),
default => throw new LogicException(
sprintf('%s must be a relationship instance.', $this::class)
)
};
};
}
public function getRelationWhereInKey(): Closure
{
return fn (): string => match (true) {
$this instanceof BelongsTo => $this->getQualifiedForeignKeyName(),
$this instanceof HasOneOrMany, $this instanceof BelongsToMany => $this->getQualifiedParentKeyName(),
$this instanceof HasOneOrManyThrough => $this->getQualifiedLocalKeyName(),
default => throw new LogicException(
sprintf('%s must be a relationship instance.', $this::class)
)
};
}
}
================================================
FILE: src/HasinServiceProvider.php
================================================
<?php
namespace BiiiiiigMonster\Hasin;
use BiiiiiigMonster\Hasin\Database\Eloquent\BuilderMixin;
use BiiiiiigMonster\Hasin\Database\Eloquent\RelationMixin;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\ServiceProvider;
class HasinServiceProvider extends ServiceProvider
{
/**
* @throws \ReflectionException
*/
public function register()
{
// Eloquent\Builder mixin, provides hasin series implementation.
Builder::mixin(new BuilderMixin());
// Eloquent\Relation mixin, support for the bottom layer of hasin.
Relation::mixin(new RelationMixin());
}
}
================================================
FILE: tests/Features/DoesntHaveInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('doesntHaveIn same as doesntHave', function () {
$doesntHave = User::doesntHave('posts')->orderBy('id')->pluck('id');
$doesntHaveIn = User::doesntHaveIn('posts')->orderBy('id')->pluck('id');
expect($doesntHave)->toEqual($doesntHaveIn);
});
test('nested doesntHaveIn same as nested doesntHave', function () {
$doesntHave = User::doesntHave('posts.comments')->orderBy('id')->pluck('id');
$doesntHaveIn = User::doesntHaveIn('posts.comments')->orderBy('id')->pluck('id');
expect($doesntHave)->toEqual($doesntHaveIn);
});
================================================
FILE: tests/Features/DoesntHaveMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('doesntHaveMorphIn same as doesntHaveMorph', function () {
$doesntHaveMorph = Comment::doesntHaveMorph('commentable', [Post::class])->orderBy('id')->pluck('id');
$doesntHaveMorphIn = Comment::doesntHaveMorphIn('commentable', [Post::class])->orderBy('id')->pluck('id');
expect($doesntHaveMorph)->toEqual($doesntHaveMorphIn);
});
================================================
FILE: tests/Features/HasInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Country;
use BiiiiiigMonster\Hasin\Tests\Models\Image;
use BiiiiiigMonster\Hasin\Tests\Models\Supplier;
use BiiiiiigMonster\Hasin\Tests\Models\User;
use BiiiiiigMonster\Hasin\Tests\Models\Video;
test('HasMany: hasIn same as has', function () {
$has = User::has('posts')->orderBy('id')->pluck('id');
$hasIn = User::hasIn('posts')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('HasOne: hasIn same as has', function () {
$has = User::has('phone')->orderBy('id')->pluck('id');
$hasIn = User::hasIn('phone')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('BelongsTo: hasIn same as has', function () {
$has = User::has('country')->orderBy('id')->pluck('id');
$hasIn = User::hasIn('country')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('BelongsToMany: hasIn same as has', function () {
$has = User::has('roles')->orderBy('id')->pluck('id');
$hasIn = User::hasIn('roles')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('HasOneThrough: hasIn same as has', function () {
$has = Supplier::has('userHistory')->orderBy('id')->pluck('id');
$hasIn = Supplier::hasIn('userHistory')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('HasManyThrough: hasIn same as has', function () {
$has = Country::has('posts')->orderBy('id')->pluck('id');
$hasIn = Country::hasIn('posts')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('MorphOne: hasIn same as has', function () {
$has = User::has('image')->orderBy('id')->pluck('id');
$hasIn = User::hasIn('image')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('MorphTo: hasIn same as has', function () {
$has = Image::has('imageable')->orderBy('id')->pluck('id');
$hasIn = Image::hasIn('imageable')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('MorphMany: hasIn same as has', function () {
$has = Video::has('comments')->orderBy('id')->pluck('id');
$hasIn = Video::hasIn('comments')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('MorphToMany: hasIn same as has', function () {
$has = Video::has('tags')->orderBy('id')->pluck('id');
$hasIn = Video::hasIn('tags')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('hasIn(gte 2) same as has(gte 2)', function () {
$has = User::has('posts', '>=', 2)->orderBy('id')->pluck('id');
$hasIn = User::hasIn('posts', '>=', 2)->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('nested hasIn same as nested has', function () {
$has = User::has('posts.comments')->orderBy('id')->pluck('id');
$hasIn = User::hasIn('posts.comments')->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
test('nested hasIn(gte 2) same as nested has(gte 2)', function () {
$has = User::has('posts.comments', '>=', 2)->orderBy('id')->pluck('id');
$hasIn = User::hasIn('posts.comments', '>=', 2)->orderBy('id')->pluck('id');
expect($has)->toEqual($hasIn);
});
================================================
FILE: tests/Features/HasMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('hasMorphIn same as hasMorph', function () {
$hasMorph = Comment::hasMorph('commentable', [Post::class])->orderBy('id')->pluck('id');
$hasMorphIn = Comment::hasMorphIn('commentable', [Post::class])->orderBy('id')->pluck('id');
expect($hasMorph)->toEqual($hasMorphIn);
});
test('hasMorphIn(gte 2) same as hasMorph(gte 2)', function () {
$hasMorph = Comment::hasMorph('commentable', [Post::class], '>=', 2)->orderBy('id')->pluck('id');
$hasMorphIn = Comment::hasMorphIn('commentable', [Post::class], '>=', 2)->orderBy('id')->pluck('id');
expect($hasMorph)->toEqual($hasMorphIn);
});
================================================
FILE: tests/Features/OrDoesntHaveInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('orDoesntHaveIn same as orDoesntHave', function () {
$orDoesntHave = User::where('age', '>', 18)->orDoesntHave('posts')->orderBy('id')->pluck('id');
$orDoesntHaveIn = User::where('age', '>', 18)->orDoesntHaveIn('posts')->orderBy('id')->pluck('id');
expect($orDoesntHave)->toEqual($orDoesntHaveIn);
});
test('nested orDoesntHaveIn same as nested orDoesntHave', function () {
$orDoesntHave = User::where('age', '>', 18)->orDoesntHave('posts.comments')->orderBy('id')->pluck('id');
$orDoesntHaveIn = User::where('age', '>', 18)->orDoesntHaveIn('posts.comments')->orderBy('id')->pluck('id');
expect($orDoesntHave)->toEqual($orDoesntHaveIn);
});
================================================
FILE: tests/Features/OrDoesntHaveMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('orDoesntHaveMorphIn same as orDoesntHaveMorph', function () {
$orDoesntHaveMorph = Comment::where('status', '>', 2)->orDoesntHaveMorph('commentable', [Post::class])->orderBy('id')->pluck('id');
$orDoesntHaveMorphIn = Comment::where('status', '>', 2)->orDoesntHaveMorphIn('commentable', [Post::class])->orderBy('id')->pluck('id');
expect($orDoesntHaveMorph)->toEqual($orDoesntHaveMorphIn);
});
================================================
FILE: tests/Features/OrHasInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('orHasIn same as orHas', function () {
$orHas = User::where('age', '>', 18)->orHas('posts')->orderBy('id')->pluck('id');
$orHasIn = User::where('age', '>', 18)->orHasIn('posts')->orderBy('id')->pluck('id');
expect($orHas)->toEqual($orHasIn);
});
test('orHasIn(gte 2) same as orHas(gte 2)', function () {
$orHas = User::where('age', '>', 18)->orHas('posts', '>=', 2)->orderBy('id')->pluck('id');
$orHasIn = User::where('age', '>', 18)->orHasIn('posts', '>=', 2)->orderBy('id')->pluck('id');
expect($orHas)->toEqual($orHasIn);
});
test('nested orHasIn same as nested orHas', function () {
$orHas = User::where('age', '>', 18)->orHas('posts.comments')->orderBy('id')->pluck('id');
$orHasIn = User::where('age', '>', 18)->orHasIn('posts.comments')->orderBy('id')->pluck('id');
expect($orHas)->toEqual($orHasIn);
});
test('nested orHasIn(gte 2) same as nested orHas(gte 2)', function () {
$orHas = User::where('age', '>', 18)->orHas('posts.comments', '>=', 2)->orderBy('id')->pluck('id');
$orHasIn = User::where('age', '>', 18)->orHasIn('posts.comments', '>=', 2)->orderBy('id')->pluck('id');
expect($orHas)->toEqual($orHasIn);
});
================================================
FILE: tests/Features/OrHasMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('orHasMorphIn same as orHasMorph', function () {
$orHasMorph = Comment::where('status', '>', 2)->orHasMorph('commentable', [Post::class])->orderBy('id')->pluck('id');
$orHasMorphIn = Comment::where('status', '>', 2)->orHasMorphIn('commentable', [Post::class])->orderBy('id')->pluck('id');
expect($orHasMorph)->toEqual($orHasMorphIn);
});
test('orHasMorphIn(gte 2) same as orHasMorph(gte 2)', function () {
$orHasMorph = Comment::where('status', '>', 2)->orHasMorph('commentable', [Post::class], '>=', 2)->orderBy('id')->pluck('id');
$orHasMorphIn = Comment::where('status', '>', 2)->orHasMorphIn('commentable', [Post::class], '>=', 2)->orderBy('id')->pluck('id');
expect($orHasMorph)->toEqual($orHasMorphIn);
});
================================================
FILE: tests/Features/OrWhereDoesntHaveInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('orWhereDoesntHaveIn same as orWhereDoesntHave', function () {
$orWhereDoesntHave = User::where('age', '>', 18)->orWhereDoesntHave('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
$orWhereDoesntHaveIn = User::where('age', '>', 18)->orWhereDoesntHaveIn('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
expect($orWhereDoesntHave)->toEqual($orWhereDoesntHaveIn);
});
test('nested orWhereDoesntHaveIn same as nested orWhereDoesntHave', function () {
$orWhereDoesntHave = User::where('age', '>', 18)->orWhereDoesntHave('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
$orWhereDoesntHaveIn = User::where('age', '>', 18)->orWhereDoesntHaveIn('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
expect($orWhereDoesntHave)->toEqual($orWhereDoesntHaveIn);
});
================================================
FILE: tests/Features/OrWhereDoesntHaveMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('orWhereDoesntHaveMorphIn same as orWhereDoesntHaveMorph', function () {
$orWhereDoesntHaveMorph = Comment::where('status', '>', 2)->orWhereDoesntHaveMorph('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
$orWhereDoesntHaveMorphIn = Comment::where('status', '>', 2)->orWhereDoesntHaveMorphIn('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
expect($orWhereDoesntHaveMorph)->toEqual($orWhereDoesntHaveMorphIn);
});
================================================
FILE: tests/Features/OrWhereHasInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('orWhereHasIn same as orWhereHas', function () {
$orWhereHas = User::where('age', '>', 18)->orWhereHas('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
$orWhereHasIn = User::where('age', '>', 18)->orWhereHasIn('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
expect($orWhereHas)->toEqual($orWhereHasIn);
});
test('nested orWhereHasIn same as nested orWhereHas', function () {
$orWhereHas = User::where('age', '>', 18)->orWhereHas('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
$orWhereHasIn = User::where('age', '>', 18)->orWhereHasIn('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
expect($orWhereHas)->toEqual($orWhereHasIn);
});
================================================
FILE: tests/Features/OrWhereHasMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('orWhereHasMorphIn same as orWhereHasMorph', function () {
$orWhereHasMorph = Comment::where('status', '>', 2)->orWhereHasMorph('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
$orWhereHasMorphIn = Comment::where('status', '>', 2)->orWhereHasMorphIn('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
expect($orWhereHasMorph)->toEqual($orWhereHasMorphIn);
});
================================================
FILE: tests/Features/OrWhereMorphRelationInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('orWhereMorphRelationIn same as orWhereMorphRelationIn', function () {
$orWhereMorphRelation = Comment::where('status', '>=', 2)
->orWhereMorphRelation('commentable', [Post::class], 'title', 'like', '%code%')
->orderBy('id')->pluck('id');
$orWhereMorphRelationIn = Comment::where('status', '>=', 2)
->orWhereMorphRelationIn('commentable', [Post::class], 'title', 'like', '%code%')
->orderBy('id')->pluck('id');
expect($orWhereMorphRelation)->toEqual($orWhereMorphRelationIn);
});
================================================
FILE: tests/Features/OrWhereRelationInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('orWhereRelationIn same as orWhereRelation', function () {
$orWhereRelation = User::where('age', '>', 18)
->orWhereRelation('posts', 'title', 'like', '%code%')
->orderBy('id')->pluck('id');
$orWhereRelationIn = User::where('age', '>', 18)
->orWhereRelationIn('posts', 'title', 'like', '%code%')
->orderBy('id')->pluck('id');
expect($orWhereRelation)->toEqual($orWhereRelationIn);
});
test('nested whereRelationIn same as nested whereRelation', function () {
$orWhereRelation = User::where('age', '>', 18)
->orWhereRelation('posts.comments', 'status', '>=', '2')
->orderBy('id')->pluck('id');
$orWhereRelationIn = User::where('age', '>', 18)
->orWhereRelationIn('posts.comments', 'status', '>=', '2')
->orderBy('id')->pluck('id');
expect($orWhereRelation)->toEqual($orWhereRelationIn);
});
================================================
FILE: tests/Features/WhereDoesntHaveInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('whereDoesntHaveIn same as whereDoesntHave', function () {
$whereDoesntHave = User::whereDoesntHave('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
$whereDoesntHaveIn = User::whereDoesntHaveIn('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
expect($whereDoesntHave)->toEqual($whereDoesntHaveIn);
});
test('nested whereDoesntHaveIn same as nested whereDoesntHave', function () {
$whereDoesntHave = User::whereDoesntHave('posts.comments', function ($query) {
$query->where('status', '>', 2);
;
})->orderBy('id')->pluck('id');
$whereDoesntHaveIn = User::whereDoesntHaveIn('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
expect($whereDoesntHave)->toEqual($whereDoesntHaveIn);
});
================================================
FILE: tests/Features/WhereDoesntHaveMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('whereDoesntHaveMorphIn same as whereDoesntHaveMorph', function () {
$whereDoesntHaveMorph = Comment::whereDoesntHaveMorph('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
$whereDoesntHaveMorphIn = Comment::whereDoesntHaveMorphIn('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
expect($whereDoesntHaveMorph)->toEqual($whereDoesntHaveMorphIn);
});
================================================
FILE: tests/Features/WhereHasInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('whereHasIn same as whereHas', function () {
$whereHas = User::whereHas('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
$whereHasIn = User::whereHasIn('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->pluck('id');
expect($whereHas)->toEqual($whereHasIn);
});
test('nested whereHasIn same as nested whereHas', function () {
$whereHas = User::whereHas('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
$whereHasIn = User::whereHasIn('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->pluck('id');
expect($whereHas)->toEqual($whereHasIn);
});
================================================
FILE: tests/Features/WhereHasMorphInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('whereHasMorphIn same as whereHasMorph', function () {
$whereHasMorph = Comment::whereHasMorph('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
$whereHasMorphIn = Comment::whereHasMorphIn('commentable', [Post::class], function ($query) {
$query->where('title', 'like', '%code%');
})->orderBy('id')->pluck('id');
expect($whereHasMorph)->toEqual($whereHasMorphIn);
});
================================================
FILE: tests/Features/WhereMorphRelationInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
test('whereMorphRelationIn same as whereMorphRelation', function () {
$whereMorphRelation = Comment::whereMorphRelation('commentable', [Post::class], 'title', 'like', '%code%')
->orderBy('id')->pluck('id');
$whereMorphRelationIn = Comment::whereMorphRelationIn('commentable', [Post::class], 'title', 'like', '%code%')
->orderBy('id')->pluck('id');
expect($whereMorphRelation)->toEqual($whereMorphRelationIn);
});
================================================
FILE: tests/Features/WhereRelationInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('whereRelationIn same as whereRelation', function () {
$whereRelation = User::whereRelation('posts', 'title', 'like', '%code%')->orderBy('id')->pluck('id');
$whereRelationIn = User::whereRelationIn('posts', 'title', 'like', '%code%')->orderBy('id')->pluck('id');
expect($whereRelation)->toEqual($whereRelationIn);
});
test('nested whereRelationIn same as nested whereRelation', function () {
$whereRelation = User::whereRelation('posts.comments', 'status', '>=', '2')->orderBy('id')->pluck('id');
$whereRelationIn = User::whereRelationIn('posts.comments', 'status', '>=', '2')->orderBy('id')->pluck('id');
expect($whereRelation)->toEqual($whereRelationIn);
});
================================================
FILE: tests/Features/WithWhereHasInTest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\Models\User;
test('withWhereHasIn same as withWhereHas', function () {
$whereHas = User::withWhereHas('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->get();
$whereHasIn = User::withWhereHasIn('posts', function ($query) {
$query->where('votes', '>', 20);
})->orderBy('id')->get();
expect($whereHas->pluck('id'))->toEqual($whereHasIn->pluck('id'))
->and($whereHas->pluck('posts.id'))->toEqual($whereHasIn->pluck('posts.id'));
});
test('nested withWhereHasIn same as nested withWhereHas', function () {
$whereHas = User::withWhereHas('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->get();
$whereHasIn = User::withWhereHasIn('posts.comments', function ($query) {
$query->where('status', '>', 2);
})->orderBy('id')->get();
expect($whereHas->pluck('id'))->toEqual($whereHasIn->pluck('id'))
->and($whereHas->pluck('posts.id'))->toEqual($whereHasIn->pluck('posts.id'))
->and($whereHas->pluck('posts.comments.id'))->toEqual($whereHasIn->pluck('posts.comments.id'));
});
================================================
FILE: tests/Models/Comment.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Comment extends Model
{
use HasFactory;
public function commentable(): MorphTo
{
return $this->morphTo();
}
}
================================================
FILE: tests/Models/Country.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
class Country extends Model
{
use HasFactory;
public function posts(): HasManyThrough
{
return $this->hasManyThrough(Post::class, User::class);
}
}
================================================
FILE: tests/Models/History.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class History extends Model
{
use HasFactory;
}
================================================
FILE: tests/Models/Image.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Image extends Model
{
use HasFactory;
public function imageable(): MorphTo
{
return $this->morphTo();
}
}
================================================
FILE: tests/Models/Phone.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
use HasFactory;
}
================================================
FILE: tests/Models/Post.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
class Post extends Model
{
use HasFactory;
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function image(): MorphOne
{
return $this->morphOne(Image::class, 'imageable');
}
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable')->using(Taggable::class);
}
}
================================================
FILE: tests/Models/Role.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Role extends Model
{
use HasFactory;
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class)->using(RoleUser::class)->withTimestamps();
}
}
================================================
FILE: tests/Models/RoleUser.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUser extends Pivot
{
use HasFactory;
}
================================================
FILE: tests/Models/Supplier.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
class Supplier extends Model
{
use HasFactory;
public function userHistory(): HasOneThrough
{
return $this->hasOneThrough(History::class, User::class);
}
}
================================================
FILE: tests/Models/Tag.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
class Tag extends Model
{
use HasFactory;
public function posts(): MorphToMany
{
return $this->morphToMany(Post::class, 'taggable')->using(Taggable::class);
}
public function videos(): MorphToMany
{
return $this->morphToMany(Video::class, 'taggable')->using(Taggable::class);
}
}
================================================
FILE: tests/Models/Taggable.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Taggable extends MorphPivot
{
use HasFactory;
}
================================================
FILE: tests/Models/User.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphOne;
class User extends Model
{
use HasFactory;
public function country(): BelongsTo
{
return $this->belongsTo(Country::class);
}
public function supplier(): BelongsTo
{
return $this->belongsTo(Supplier::class);
}
public function phone(): HasOne
{
return $this->hasOne(Phone::class);
}
public function history(): HasOne
{
return $this->hasOne(History::class);
}
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)->using(RoleUser::class)->withTimestamps();
}
public function image(): MorphOne
{
return $this->morphOne(Image::class, 'imageable');
}
}
================================================
FILE: tests/Models/Video.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
class Video extends Model
{
use HasFactory;
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable')->using(Taggable::class);
}
}
================================================
FILE: tests/Pest.php
================================================
<?php
use BiiiiiigMonster\Hasin\Tests\TestCase;
uses(TestCase::class)->in(__DIR__);
================================================
FILE: tests/TestCase.php
================================================
<?php
namespace BiiiiiigMonster\Hasin\Tests;
use BiiiiiigMonster\Hasin\HasinServiceProvider;
use BiiiiiigMonster\Hasin\Tests\Models\Comment;
use BiiiiiigMonster\Hasin\Tests\Models\Country;
use BiiiiiigMonster\Hasin\Tests\Models\History;
use BiiiiiigMonster\Hasin\Tests\Models\Image;
use BiiiiiigMonster\Hasin\Tests\Models\Phone;
use BiiiiiigMonster\Hasin\Tests\Models\Post;
use BiiiiiigMonster\Hasin\Tests\Models\Role;
use BiiiiiigMonster\Hasin\Tests\Models\Supplier;
use BiiiiiigMonster\Hasin\Tests\Models\Tag;
use BiiiiiigMonster\Hasin\Tests\Models\User;
use BiiiiiigMonster\Hasin\Tests\Models\Video;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
use Orchestra\Testbench\TestCase as Orchestra;
class TestCase extends Orchestra
{
private Migration $migration;
protected function getPackageProviders($app)
{
return [
HasinServiceProvider::class,
];
}
protected function defineDatabaseMigrations()
{
$this->migration->up();
}
protected function destroyDatabaseMigrations()
{
$this->migration->down();
}
protected function defineDatabaseSeeders()
{
$tags = Tag::factory(20)->create();
$countries = Country::factory(15)->create();
$suppliers = Supplier::factory(15)->create();
$roles = Role::factory(10)->create();
$users = User::factory(15)
->has(History::factory())
->has(Phone::factory())
->has(Image::factory(3))
->hasAttached($roles->random(5))
->sequence(fn () => ['country_id' => $countries->pluck('id')->random()])
->sequence(fn () => ['supplier_id' => $suppliers->pluck('id')->random()])
->create();
$posts = Post::factory(15)
->sequence(fn () => ['user_id' => $users->pluck('id')->random()])
->hasAttached($tags->random(15))
->create();
$videos = Video::factory(15)->hasAttached($tags->random(15))->create();
$posts->random(5)->map(function ($post) {
Comment::factory(3)->for($post, 'commentable')->create();
Image::factory(2)->for($post, 'imageable')->create();
});
$videos->random(5)->map(function ($video) {
Comment::factory(3)->for($video, 'commentable')->create();
});
}
public function getEnvironmentSetUp($app)
{
config()->set('database.connections.mysql.prefix', 'hasin_test_');
Schema::defaultStringLength(191);
Factory::guessFactoryNamesUsing(
fn (string $modelName) => 'BiiiiiigMonster\\Hasin\\Database\\Factories\\'.class_basename($modelName).'Factory'
);
$this->migration = include __DIR__.'/../database/migrations/create_hasin_test_table.php';
}
}
gitextract_znsrf2cn/
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── config.yml
│ ├── dependabot.yml
│ └── workflows/
│ ├── dependabot-auto-merge.yml
│ ├── fix-php-code-style-issues.yml
│ └── run-tests.yml
├── .gitignore
├── .scrutinizer.yml
├── CHANGELOG.md
├── LICENSE
├── README-CN.md
├── README.md
├── _laravel_ide_helper.php
├── composer.json
├── database/
│ ├── factories/
│ │ ├── CommentFactory.php
│ │ ├── CountryFactory.php
│ │ ├── HistoryFactory.php
│ │ ├── ImageFactory.php
│ │ ├── PhoneFactory.php
│ │ ├── PostFactory.php
│ │ ├── RoleFactory.php
│ │ ├── SupplierFactory.php
│ │ ├── TagFactory.php
│ │ ├── UserFactory.php
│ │ └── VideoFactory.php
│ └── migrations/
│ └── create_hasin_test_table.php
├── phpunit.xml.dist
├── pint.json
├── src/
│ ├── Database/
│ │ └── Eloquent/
│ │ ├── BuilderMixin.php
│ │ └── RelationMixin.php
│ └── HasinServiceProvider.php
└── tests/
├── Features/
│ ├── DoesntHaveInTest.php
│ ├── DoesntHaveMorphInTest.php
│ ├── HasInTest.php
│ ├── HasMorphInTest.php
│ ├── OrDoesntHaveInTest.php
│ ├── OrDoesntHaveMorphInTest.php
│ ├── OrHasInTest.php
│ ├── OrHasMorphInTest.php
│ ├── OrWhereDoesntHaveInTest.php
│ ├── OrWhereDoesntHaveMorphInTest.php
│ ├── OrWhereHasInTest.php
│ ├── OrWhereHasMorphInTest.php
│ ├── OrWhereMorphRelationInTest.php
│ ├── OrWhereRelationInTest.php
│ ├── WhereDoesntHaveInTest.php
│ ├── WhereDoesntHaveMorphInTest.php
│ ├── WhereHasInTest.php
│ ├── WhereHasMorphInTest.php
│ ├── WhereMorphRelationInTest.php
│ ├── WhereRelationInTest.php
│ └── WithWhereHasInTest.php
├── Models/
│ ├── Comment.php
│ ├── Country.php
│ ├── History.php
│ ├── Image.php
│ ├── Phone.php
│ ├── Post.php
│ ├── Role.php
│ ├── RoleUser.php
│ ├── Supplier.php
│ ├── Tag.php
│ ├── Taggable.php
│ ├── User.php
│ └── Video.php
├── Pest.php
└── TestCase.php
SYMBOL INDEX (113 symbols across 30 files)
FILE: _laravel_ide_helper.php
class Builder (line 8) | class Builder
method hasIn (line 24) | public function hasIn($relation, $operator = '>=', $count = 1, $boolea...
method orHasIn (line 39) | public function orHasIn($relation, $operator = '>=', $count = 1)
method doesntHaveIn (line 54) | public function doesntHaveIn($relation, $boolean = 'and', Closure $cal...
method orDoesntHaveIn (line 66) | public function orDoesntHaveIn()
method whereHasIn (line 82) | public function whereHasIn($relation, Closure $callback = null, $opera...
method orWhereHasIn (line 98) | public function orWhereHasIn($relation, Closure $callback = null, $ope...
method whereDoesntHaveIn (line 112) | public function whereDoesntHaveIn($relation, Closure $callback = null)
method orWhereDoesntHaveIn (line 126) | public function orWhereDoesntHaveIn($relation, Closure $callback = null)
method hasMorphIn (line 144) | public function hasMorphIn($relation, $types, $operator = '>=', $count...
method orHasMorphIn (line 160) | public function orHasMorphIn($relation, $types, $operator = '>=', $cou...
method doesntHaveMorphIn (line 176) | public function doesntHaveMorphIn($relation, $types, $boolean = 'and',...
method orDoesntHaveMorphIn (line 190) | public function orDoesntHaveMorphIn($relation, $types)
method whereHasMorphIn (line 202) | public function whereHasMorphIn()
method orWhereHasMorphIn (line 219) | public function orWhereHasMorphIn($relation, $types, Closure $callback...
method whereDoesntHaveMorphIn (line 234) | public function whereDoesntHaveMorphIn($relation, $types, Closure $cal...
method orWhereDoesntHaveMorphIn (line 249) | public function orWhereDoesntHaveMorphIn($relation, $types, Closure $c...
method whereRelationIn (line 263) | public function whereRelationIn($relation, $column, $operator = null, ...
method orWhereRelationIn (line 277) | public function orWhereRelationIn($relation, $column, $operator = null...
method whereMorphRelationIn (line 292) | public function whereMorphRelationIn($relation, $types, $column, $oper...
method orWhereMorphRelationIn (line 307) | public function orWhereMorphRelationIn($relation, $types, $column, $op...
FILE: database/factories/CommentFactory.php
class CommentFactory (line 8) | class CommentFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/CountryFactory.php
class CountryFactory (line 8) | class CountryFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/HistoryFactory.php
class HistoryFactory (line 8) | class HistoryFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/ImageFactory.php
class ImageFactory (line 8) | class ImageFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/PhoneFactory.php
class PhoneFactory (line 8) | class PhoneFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/PostFactory.php
class PostFactory (line 8) | class PostFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/RoleFactory.php
class RoleFactory (line 8) | class RoleFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/SupplierFactory.php
class SupplierFactory (line 8) | class SupplierFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/TagFactory.php
class TagFactory (line 8) | class TagFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/UserFactory.php
class UserFactory (line 8) | class UserFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/factories/VideoFactory.php
class VideoFactory (line 8) | class VideoFactory extends Factory
method definition (line 17) | public function definition()
FILE: database/migrations/create_hasin_test_table.php
method up (line 13) | public function up()
method down (line 99) | public function down()
FILE: src/Database/Eloquent/BuilderMixin.php
class BuilderMixin (line 11) | class BuilderMixin
method hasIn (line 18) | public function hasIn(): Closure
method hasInNested (line 70) | protected function hasInNested(): Closure
method orHasIn (line 101) | public function orHasIn(): Closure
method doesntHaveIn (line 114) | public function doesntHaveIn(): Closure
method orDoesntHaveIn (line 127) | public function orDoesntHaveIn(): Closure
method whereHasIn (line 140) | public function whereHasIn(): Closure
method withWhereHasIn (line 155) | public function withWhereHasIn(): Closure
method orWhereHasIn (line 169) | public function orWhereHasIn(): Closure
method whereDoesntHaveIn (line 182) | public function whereDoesntHaveIn(): Closure
method orWhereDoesntHaveIn (line 195) | public function orWhereDoesntHaveIn(): Closure
method hasMorphIn (line 208) | public function hasMorphIn(): Closure
method orHasMorphIn (line 250) | public function orHasMorphIn(): Closure
method doesntHaveMorphIn (line 263) | public function doesntHaveMorphIn(): Closure
method orDoesntHaveMorphIn (line 276) | public function orDoesntHaveMorphIn(): Closure
method whereHasMorphIn (line 289) | public function whereHasMorphIn(): Closure
method orWhereHasMorphIn (line 302) | public function orWhereHasMorphIn(): Closure
method whereDoesntHaveMorphIn (line 315) | public function whereDoesntHaveMorphIn(): Closure
method orWhereDoesntHaveMorphIn (line 328) | public function orWhereDoesntHaveMorphIn(): Closure
method whereRelationIn (line 341) | public function whereRelationIn(): Closure
method orWhereRelationIn (line 359) | public function orWhereRelationIn(): Closure
method whereMorphRelationIn (line 377) | public function whereMorphRelationIn(): Closure
method orWhereMorphRelationIn (line 391) | public function orWhereMorphRelationIn(): Closure
method addHasInWhere (line 405) | protected function addHasInWhere(): Closure
FILE: src/Database/Eloquent/RelationMixin.php
class RelationMixin (line 17) | class RelationMixin
method getRelationExistenceInQuery (line 19) | public function getRelationExistenceInQuery(): Closure
method getRelationWhereInKey (line 143) | public function getRelationWhereInKey(): Closure
FILE: src/HasinServiceProvider.php
class HasinServiceProvider (line 11) | class HasinServiceProvider extends ServiceProvider
method register (line 16) | public function register()
FILE: tests/Models/Comment.php
class Comment (line 9) | class Comment extends Model
method commentable (line 13) | public function commentable(): MorphTo
FILE: tests/Models/Country.php
class Country (line 9) | class Country extends Model
method posts (line 13) | public function posts(): HasManyThrough
FILE: tests/Models/History.php
class History (line 8) | class History extends Model
FILE: tests/Models/Image.php
class Image (line 9) | class Image extends Model
method imageable (line 13) | public function imageable(): MorphTo
FILE: tests/Models/Phone.php
class Phone (line 8) | class Phone extends Model
FILE: tests/Models/Post.php
class Post (line 12) | class Post extends Model
method user (line 16) | public function user(): BelongsTo
method image (line 21) | public function image(): MorphOne
method comments (line 26) | public function comments(): MorphMany
method tags (line 31) | public function tags(): MorphToMany
FILE: tests/Models/Role.php
class Role (line 9) | class Role extends Model
method users (line 13) | public function users(): BelongsToMany
FILE: tests/Models/RoleUser.php
class RoleUser (line 8) | class RoleUser extends Pivot
FILE: tests/Models/Supplier.php
class Supplier (line 9) | class Supplier extends Model
method userHistory (line 13) | public function userHistory(): HasOneThrough
FILE: tests/Models/Tag.php
class Tag (line 9) | class Tag extends Model
method posts (line 13) | public function posts(): MorphToMany
method videos (line 18) | public function videos(): MorphToMany
FILE: tests/Models/Taggable.php
class Taggable (line 8) | class Taggable extends MorphPivot
FILE: tests/Models/User.php
class User (line 13) | class User extends Model
method country (line 17) | public function country(): BelongsTo
method supplier (line 22) | public function supplier(): BelongsTo
method phone (line 27) | public function phone(): HasOne
method history (line 32) | public function history(): HasOne
method posts (line 37) | public function posts(): HasMany
method roles (line 42) | public function roles(): BelongsToMany
method image (line 47) | public function image(): MorphOne
FILE: tests/Models/Video.php
class Video (line 10) | class Video extends Model
method comments (line 14) | public function comments(): MorphMany
method tags (line 19) | public function tags(): MorphToMany
FILE: tests/TestCase.php
class TestCase (line 22) | class TestCase extends Orchestra
method getPackageProviders (line 26) | protected function getPackageProviders($app)
method defineDatabaseMigrations (line 33) | protected function defineDatabaseMigrations()
method destroyDatabaseMigrations (line 38) | protected function destroyDatabaseMigrations()
method defineDatabaseSeeders (line 43) | protected function defineDatabaseSeeders()
method getEnvironmentSetUp (line 76) | public function getEnvironmentSetUp($app)
Condensed preview — 67 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (99K chars).
[
{
"path": ".gitattributes",
"chars": 807,
"preview": "# Path-based git attributes\n# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html\n\n# Ignore all test and"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 608,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Ask a question\n url: https://github.com/biiiiiigmonster/hasin/di"
},
{
"path": ".github/dependabot.yml",
"chars": 321,
"preview": "# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repositor"
},
{
"path": ".github/workflows/dependabot-auto-merge.yml",
"chars": 1054,
"preview": "name: dependabot-auto-merge\non: pull_request_target\n\npermissions:\n pull-requests: write\n contents: write\n\njobs:\n depe"
},
{
"path": ".github/workflows/fix-php-code-style-issues.yml",
"chars": 449,
"preview": "name: Fix PHP code style issues\n\non: [push]\n\njobs:\n php-code-styling:\n runs-on: ubuntu-latest\n\n steps:\n - na"
},
{
"path": ".github/workflows/run-tests.yml",
"chars": 1711,
"preview": "name: run-tests\n\non:\n push:\n branches:\n - master\n pull_request:\n branches:\n - master\n\njobs:\n test:\n "
},
{
"path": ".gitignore",
"chars": 124,
"preview": ".idea\n.phpunit.result.cache\nbuild\ncomposer.lock\ncoverage\ndocs\nphpunit.xml\ntestbench.yaml\nvendor\nnode_modules\n.phpunit.ca"
},
{
"path": ".scrutinizer.yml",
"chars": 505,
"preview": "checks:\n php:\n code_rating: true\n remove_extra_empty_lines: true\n remove_php_closing_tag: true\n remove_trai"
},
{
"path": "CHANGELOG.md",
"chars": 77,
"preview": "# Changelog\n\nAll notable changes to `hasin` will be documented in this file.\n"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "The MIT License (MIT)\n\nCopyright (c) Yunfeng Lu\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README-CN.md",
"chars": 4199,
"preview": "[English](./README.md) | 中文\n\n<div align=\"center\">\n\n# LARAVEL HASIN\n\n[\n\n<div align=\"center\">\n\n# LARAVEL HASIN\n\n[ {\n class Builder\n {\n "
},
{
"path": "composer.json",
"chars": 1317,
"preview": "{\n \"name\": \"biiiiiigmonster/hasin\",\n \"description\": \"Laravel framework relation has in implement\",\n \"type\": \"li"
},
{
"path": "database/factories/CommentFactory.php",
"chars": 508,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse Illumina"
},
{
"path": "database/factories/CountryFactory.php",
"chars": 444,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Country;\nuse Illumina"
},
{
"path": "database/factories/HistoryFactory.php",
"chars": 447,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\History;\nuse Illumina"
},
{
"path": "database/factories/ImageFactory.php",
"chars": 433,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Image;\nuse Illuminate"
},
{
"path": "database/factories/PhoneFactory.php",
"chars": 450,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Phone;\nuse Illuminate"
},
{
"path": "database/factories/PostFactory.php",
"chars": 495,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\nuse Illuminate\\"
},
{
"path": "database/factories/RoleFactory.php",
"chars": 432,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Role;\nuse Illuminate\\"
},
{
"path": "database/factories/SupplierFactory.php",
"chars": 444,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Supplier;\nuse Illumin"
},
{
"path": "database/factories/TagFactory.php",
"chars": 429,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Tag;\nuse Illuminate\\D"
},
{
"path": "database/factories/UserFactory.php",
"chars": 499,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\nuse Illuminate\\"
},
{
"path": "database/factories/VideoFactory.php",
"chars": 435,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Factories;\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Video;\nuse Illuminate"
},
{
"path": "database/migrations/create_hasin_test_table.php",
"chars": 3943,
"preview": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Fa"
},
{
"path": "phpunit.xml.dist",
"chars": 1398,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noName"
},
{
"path": "pint.json",
"chars": 66,
"preview": "{\n \"preset\": \"psr12\",\n \"exclude\": [\n \"build\"\n ]\n}\n"
},
{
"path": "src/Database/Eloquent/BuilderMixin.php",
"chars": 14477,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Eloquent;\n\nuse Closure;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Il"
},
{
"path": "src/Database/Eloquent/RelationMixin.php",
"chars": 7019,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Database\\Eloquent;\n\nuse Closure;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Il"
},
{
"path": "src/HasinServiceProvider.php",
"chars": 682,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin;\n\nuse BiiiiiigMonster\\Hasin\\Database\\Eloquent\\BuilderMixin;\nuse BiiiiiigMonster\\H"
},
{
"path": "tests/Features/DoesntHaveInTest.php",
"chars": 602,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('doesntHaveIn same as doesntHave', function () {\n $doesntHa"
},
{
"path": "tests/Features/DoesntHaveMorphInTest.php",
"chars": 447,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('doesntHaveMor"
},
{
"path": "tests/Features/HasInTest.php",
"chars": 3173,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Country;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Image;\nuse BiiiiiigMonster"
},
{
"path": "tests/Features/HasMorphInTest.php",
"chars": 714,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('hasMorphIn sa"
},
{
"path": "tests/Features/OrDoesntHaveInTest.php",
"chars": 727,
"preview": "<?php\n\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('orDoesntHaveIn same as orDoesntHave', function () {\n $orD"
},
{
"path": "tests/Features/OrDoesntHaveMorphInTest.php",
"chars": 513,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('orDoesntHaveM"
},
{
"path": "tests/Features/OrHasInTest.php",
"chars": 1240,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('orHasIn same as orHas', function () {\n $orHas = User::wher"
},
{
"path": "tests/Features/OrHasMorphInTest.php",
"chars": 846,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('orHasMorphIn "
},
{
"path": "tests/Features/OrWhereDoesntHaveInTest.php",
"chars": 1079,
"preview": "<?php\n\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('orWhereDoesntHaveIn same as orWhereDoesntHave', function () "
},
{
"path": "tests/Features/OrWhereDoesntHaveMorphInTest.php",
"chars": 707,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('orWhereDoesnt"
},
{
"path": "tests/Features/OrWhereHasInTest.php",
"chars": 967,
"preview": "<?php\n\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('orWhereHasIn same as orWhereHas', function () {\n $orWhere"
},
{
"path": "tests/Features/OrWhereHasMorphInTest.php",
"chars": 651,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('orWhereHasMor"
},
{
"path": "tests/Features/OrWhereMorphRelationInTest.php",
"chars": 631,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('orWhereMorphR"
},
{
"path": "tests/Features/OrWhereRelationInTest.php",
"chars": 938,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('orWhereRelationIn same as orWhereRelation', function () {\n "
},
{
"path": "tests/Features/WhereDoesntHaveInTest.php",
"chars": 964,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('whereDoesntHaveIn same as whereDoesntHave', function () {\n "
},
{
"path": "tests/Features/WhereDoesntHaveMorphInTest.php",
"chars": 641,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('whereDoesntHa"
},
{
"path": "tests/Features/WhereHasInTest.php",
"chars": 843,
"preview": "<?php\n\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('whereHasIn same as whereHas', function () {\n $whereHas = "
},
{
"path": "tests/Features/WhereHasMorphInTest.php",
"chars": 585,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('whereHasMorph"
},
{
"path": "tests/Features/WhereMorphRelationInTest.php",
"chars": 543,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Comment;\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\Post;\n\ntest('whereMorphRel"
},
{
"path": "tests/Features/WhereRelationInTest.php",
"chars": 746,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('whereRelationIn same as whereRelation', function () {\n $wh"
},
{
"path": "tests/Features/WithWhereHasInTest.php",
"chars": 1176,
"preview": "<?php\n\n\nuse BiiiiiigMonster\\Hasin\\Tests\\Models\\User;\n\ntest('withWhereHasIn same as withWhereHas', function () {\n $whe"
},
{
"path": "tests/Models/Comment.php",
"chars": 343,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Country.php",
"chars": 382,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/History.php",
"chars": 202,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Image.php",
"chars": 339,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Phone.php",
"chars": 200,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Post.php",
"chars": 878,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Role.php",
"chars": 405,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/RoleUser.php",
"chars": 213,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Supplier.php",
"chars": 389,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Tag.php",
"chars": 532,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Taggable.php",
"chars": 223,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/User.php",
"chars": 1217,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Models/Video.php",
"chars": 565,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illumin"
},
{
"path": "tests/Pest.php",
"chars": 86,
"preview": "<?php\n\nuse BiiiiiigMonster\\Hasin\\Tests\\TestCase;\n\nuses(TestCase::class)->in(__DIR__);\n"
},
{
"path": "tests/TestCase.php",
"chars": 2879,
"preview": "<?php\n\nnamespace BiiiiiigMonster\\Hasin\\Tests;\n\nuse BiiiiiigMonster\\Hasin\\HasinServiceProvider;\nuse BiiiiiigMonster\\Hasin"
}
]
About this extraction
This page contains the full source code of the biiiiiigmonster/hasin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 67 files (88.0 KB), approximately 24.2k tokens, and a symbol index with 113 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.