[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  pull_request:\n  push:\n    branches:\n      - master\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        php_version: [7.4, 8.2]\n        laravel_version: [8.*, 10.*, 11.*]\n\n    steps:\n      - name: Checkout commit\n        uses: actions/checkout@v2\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php_version }}\n\n      - name: Validate composer.json\n        run: composer validate\n      \n      - name: Run composer install\n        run: composer install --no-interaction --no-suggest\n        \n      - name: Run find-and-replace to replace * with 0\n        uses: mad9000/actions-find-and-replace-string@1\n        id: laravel_version_cleaned\n        with:\n          source: ${{ matrix.laravel_version }}\n          find: '*'\n          replace: '0'\n\n      - name: Install Laravel\n        run: composer update --no-interaction illuminate/database:^${{ steps.laravel_version_cleaned.outputs.value }}\n    \n      - name: Run PHPUnit\n        run: ./vendor/bin/phpunit\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor\n/.idea\ncomposer.lock\n.php_cs.cache\n.phpunit.result.cache\n"
  },
  {
    "path": ".php_cs",
    "content": "<?php\n\nuse PhpCsFixer\\Config;\nuse PhpCsFixer\\Finder;\n\n/*\n * Define folders to fix\n */\n$finder = Finder::create()\n    ->in([\n        __DIR__ .'/src',\n        __DIR__ .'/tests',\n    ]);\n;\n\n/*\n * Do the magic\n */\nreturn Config::create()\n    ->setUsingCache(false)\n    ->setRules([\n        '@PSR2'              => true,\n        '@Symfony'           => true,\n    ])\n    ->setFinder($finder)\n;"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Filip Horvat\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![Tests status](https://github.com/fico7489/laravel-eloquent-join/workflows/Test/badge.svg)\n\n# Laravel Eloquent Join\n\nThis package introduces the join magic for eloquent models and relations.\n\n## Introduction\n\nEloquent is a powerful ORM but its join capabilities are very poor.\n\n#### First Eloquent Problem (sorting)\n\nWith laravel you can't perform sorting of the relationship fields without manually joining related table which is very awkward. Let me give you a few reasons why. If you have a table with **posts** and related **categories** your code might look like this:\n\n```\n$posts = Post::select('posts.*')\n    ->join('categories', 'categories.id', '=', 'posts.category_id')\n    ->groupBy('posts.id')\n    ->where('categories.deleted_at', '=', null)\n    ->orderBy('categories.name');\n    \nif(request()->get('date')){\n    $posts->where('posts.date', $date)\n}\n\n$posts = $posts->get();\n```\n\n1.The first problem is that you need to worry about select.\n```\n    ->select('posts.*')\n```\nReason : without **select** id from the category can be selected and hydrated into the Post model.\n\n2.The second problem is that you need to worry about **groupBy**.\n\n    ->groupBy('posts.id');\n    \nReason : if the relation is HasOne and there are more than one categories for the post, the query will return more rows for categories.\n\n3.The third problem is that you need to change all other where clauses from : \n```\n    ->where('date', $date)\n```\nto\n```\n    ->where('posts.date', $date)\n```\nReason : a **post** and **category** can have \"date\" attribute and in that case without selecting an attribute with table \"ambiguous column\" error will be thrown.\n\n4.The fourth problem is that you are using table names(not models) and this is also bad and awkward.\n```\n    ->where('posts.date', $date)\n```\n5.The fifth problem is that you need to worry about soft deletes for joined tables. If the **category** is using SoftDeletes trait you must add : \n```\n    ->where('categories.deleted_at', '=', null)\n```\nThis package will take care of all above problems for you. \nUnlike **sorting**, you can perform **filtering** on the relationship fields without joining related tables, but this package will give you the ability to do this easier.\n\n\n#### Second Eloquent Problem (subqueries)\n\nWith laravel you can perform where on the relationship attribute but laravel will generate subqueries which are more slower than joins. \nWith this package you will be available to perform where on the relationship with joins in an elegant way.\n\n\n## Requirements\n\n| Laravel Version | Package Tag | Supported | Development Branch\n|-----------------|-------------|-----------| -----------|\n| >= 5.5.0 | 4.* | yes | master\n| < 5.5.0 | - | no | -\n\nPackage is also tested for SQLite, MySql and PostgreSql\n\n## Installation & setup\n\n1.Install package with composer\n```\ncomposer require fico7489/laravel-eloquent-join\n```\nWith this statement, a composer will install highest available package version for your current laravel version.\n\n2.Use Fico7489\\Laravel\\EloquentJoin\\Traits\\EloquentJoinTrait trait in your base model or only in particular models.\n\n```\n...\nuse Fico7489\\Laravel\\EloquentJoin\\Traits\\EloquentJoin;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nabstract class BaseModel extends Model\n{\n    use EloquentJoin;\n...\n```\n\n3.IMPORTANT\n\nFor **MySql** make sure that **strict** configuration is set to **false**\n\nconfig/database.php\n\n```\n        'mysql' => [\n\t\t\t...\n            'strict'    => false,\n\t\t\t...\n```\n\nand that's it, you are ready to go.\n\n## Options\n\nOptions can be set in the model  : \n\n```\nclass Seller extends BaseModel\n{\n    protected $useTableAlias = false;\n    protected $appendRelationsCount = false;\n    protected $leftJoin = false;\n    protected $aggregateMethod = 'MAX';\n```\n\nor on query : \n\n```\n    Order::setUseTableAlias(true)->get();\n    Order::setAppendRelationsCount(true)->get();\n    Order::setLeftJoin(true)->get();\n    Order::setAggregateMethod(true)->get();\n```\n\n#### **useTableAlias**\n\nShould we use an alias for joined tables (default = false)\n\nWith **true** query will look like this : \n```\nselect \"sellers\".* from \"sellers\" \n    left join \"locations\" as \"5b5c093d2e00f\" \n\t...\n```\n\nWith **false** query will look like this : \n```\nselect \"sellers\".* \n\tfrom \"sellers\" \n\tleft join \"locations\"                    \n\t...\n```\n\nAlias is a randomly generated string.\n\n#### **appendRelationsCount**\n\nShould we automatically append relation count field to results  (default = false)\n\nWith **true** query will look like this : \n```\nselect \"sellers\".*, count(locations.id) AS locations_count\n\tfrom \"sellers\" \n\tleft join \"locations\" as \"5b5c093d2e00f\" \n\t...\n```\n\nEach **relation** is glued with an underscore and at the end **_count** prefix is added. For example for \n\n    ->joinRelations('seller.locations')\n    \nfield would be __seller_locations_count__\n\n#### **leftJoin**\n\nShould we use **inner join** or **left join** (default = true)\n\n```\nselect \"sellers\".* \n\tfrom \"sellers\" \n\tinner join \"locations\"                    \n\t...\n```\n\nvs\n\n```\nselect \"sellers\".* \n\tfrom \"sellers\" \n\tleft join \"locations\"                    \n\t...\n```\n\n#### **aggregateMethod**\n\nWhich aggregate method to use for ordering (default = 'MAX'). \n\nWhen join is performed on the joined table we must apply aggregate functions on the sorted field so we could perform group by clause and prevent duplication of results.\n\n```\nselect \"sellers\".*, MAX(\"locations\" .\"number\") AS sort\n\tfrom \"sellers\" \n\tleft join \"locations\" \n\tgroup by \"locations\" .\"id\"\n\torder by sort\n\t...\n```\n\nOptions are : **SUM**, **AVG**, **MAX**, **MIN**, **COUNT**\n\n## Usage\n\n### Currently available relations for join queries\n\n* **BelongsTo**\n* **HasOne**\n* **HasMany**\n\n### New clauses for eloquent builder on BelongsTo and HasOne relations : \n\n **joinRelations($relations, $leftJoin = null)**\n\n* ***$relations*** which relations to join\n* ***$leftJoin*** use **left join** or **inner join**, default **left join**\n\n**orderByJoin($column, $direction  = 'asc', $aggregateMethod = null)**\n\n* ***$column*** and ***$direction***  arguments are the same as in default eloquent **orderBy()**\n* ***$aggregateMethod*** argument defines which aggregate method to use ( **SUM**, **AVG**, **MAX**, **MIN**, **COUNT**), default **MAX**\n    \n**whereJoin($column, $operator, $value, $boolean = 'and')**\n\n* arguments are the same as in default eloquent **where()**\n    \n**orWhereJoin($column, $operator, $value)**\n\n* arguments are the same as in default eloquent **orWhere()**\n\n\n**whereInJoin($column, $values, $boolean = 'and', $not = false)**\n\n* arguments are the same as in default eloquent **whereIn()**\n\n**whereNotInJoin($column, $values, $boolean = 'and')**\n\n* arguments are the same as in default eloquent **whereNotIn()**\n\n**orWhereInJoin($column, $values)**\n\n* arguments are the same as in default eloquent **orWhereIn()**\n\n**orWhereNotInJoin($column, $values)**\n\n* arguments are the same as in default eloquent **orWhereNotIn()**\n\n\n### Allowed clauses on BelongsTo, HasOne and HasMany relations on which you can use join clauses on the query\n\n* Relations that you want to use for join queries can only have these clauses : **where**, **orWhere**, **withTrashed**, **onlyTrashed**, **withoutTrashed**. \n* Clauses **where** and **orWhere** can only have these variations \n** **->where($column, $operator, $value)** \n** **->where([$column => $value])**\n* Closures are not allowed.\n* Other clauses like **whereHas**, **orderBy** etc. are not allowed.\n* You can add not allowed clauses on relations and use them in the normal eloquent way, but in these cases, you can't use those relations for join queries.\n\nAllowed relation:\n\n```\npublic function locationPrimary()\n{\n    return $this->hasOne(Location::class)\n        ->where('is_primary', '=', 1)\n        ->orWhere('is_primary', '=', 1)\n        ->withTrashed();\n}\n```\nNot allowed relation: \n\n```\npublic function locationPrimary()\n{\n    return $this->hasOne(Location::class)\n        ->where('is_primary', '=', 1)\n        ->orWhere('is_primary', '=', 1)\n        ->withTrashed()\n        ->whereHas('state', function($query){return $query;}\n        ->orderBy('name')\n        ->where(function($query){\n            return $query->where('is_primary', '=', 1);\n        });\n}\n```\n\nThe reason why the second relation is not allowed is that this package should apply all those clauses on the join clause,  eloquent use all those clauses isolated with subqueries NOT on join clause and that is more simpler to do.\n\nYou might get a picture that there are too many rules and restriction, but it is really not like that. \nDon't worry, if you do create the query that is not allowed appropriate exception will be thrown and you will know what happened.\n\n### Other \n\n* If the model uses the SoftDelete trait, where deleted_at != null will be automatically applied\n* You can combine new clauses unlimited times\n* If you combine clauses more times on same relation package will join related table only once\n\n```\nSeller::whereJoin('city.title', '=', 'test')\n    ->orWhereJoin('city.title', '=', 'test2');\n```\n\n* You can call  new clauses inside closures\n\n```\nSeller::where(function ($query) {\n    $query\n        ->whereJoin('city.title', '=', 'test')\n        ->orWhereJoin('city.title', '=', 'test2');\n});\n```\n\n* You can combine join clauses e.g. whereJoin() with eloquent clauses e.g. orderBy()\n\n```\nSeller::whereJoin('title', '=', 'test')\n    ->whereJoin('city.title', '=', 'test')\n    ->orderByJoin('city.title')\n    ->get();\n```\n\n## See action on real example\n\nDatabase schema : \n\n![Database schema](https://raw.githubusercontent.com/fico7489/laravel-eloquent-join/master/readme/era.png)\n\nModels : \n\n```\nclass Seller extends BaseModel\n{\n    public function locations()\n    {\n        return $this->hasMany(Location::class);\n    }\n    \n    public function locationPrimary()\n    {\n        return $this->hasOne(Location::class)\n            ->where('is_primary', '=', 1);\n    }\n\n    public function city()\n    {\n        return $this->belongsTo(City::class);\n    }\n```\n```\nclass Location extends BaseModel\n{\n    public function locationAddressPrimary()\n    {\n        return $this->hasOne(LocationAddress::class)\n            ->where('is_primary', '=', 1);\n    }\n    \n```\n```\nclass City extends BaseModel\n{\n    public function state()\n    {\n        return $this->belongsTo(State::class);\n    }\n}\n```\n\n### Join\n\n##### Join BelongsTo\n```Seller::joinRelations('city')```\n\n##### Join HasOne\n```Seller::joinRelations('locationPrimary')```\n\n##### Join HasMany\n```Seller::joinRelations('locations')```\n\n##### Join Mixed\n```Seller::joinRelations('city.state')```\n\n### Join (mix left join)\n\n```Seller::joinRelations('city', true)->joinRelations('city.state', false)```\n\n### Join (multiple relationships)\n\n```Seller::join(['city.state', 'locations'])```\n\n### Ordering\n\n##### Order BelongsTo\n```Seller::orderByJoin('city.title')```\n\n##### Order HasOne\n```Seller::orderByJoin('locationPrimary.address')```\n\n##### Order HasMany\n```Seller::orderByJoin('locations.title')```\n\n##### Order Mixed\n```Seller::orderByJoin('city.state.title')```\n\n### Ordering (special cases with aggregate functions)\n\n##### Order by relation count\n```Seller::orderByJoin('locations.id', 'asc', 'COUNT')```\n\n##### Order by relation field SUM\n```Seller::orderByJoin('locations.is_primary', 'asc', 'SUM')```\n\n##### Order by relation field AVG\n```Seller::orderByJoin('locations.is_primary', 'asc', 'AVG')```\n\n##### Order by relation field MAX\n```Seller::orderByJoin('locations.is_primary', 'asc', 'MAX')```\n\n##### Order by relation field MIN\n```Seller::orderByJoin('locations.is_primary', 'asc', 'MIN')```\n\n### Filtering (where or orWhere)\n\n##### Filter BelongsTo\n```Seller::whereJoin('city.title', '=', 'test')```\n\n##### Filter HasOne\n```Seller::whereJoin('locationPrimary.address', '=', 'test')```\n\n##### Filter HasMany\n```Seller::whereJoin('locations.title', '=', 'test')```\n\n##### Filter Mixed\n```Seller::whereJoin('city.state.title', '=', 'test')```\n\n### Relation count\n\n```\n$sellers = Seller::setAppendRelationsCount(true)->join('locations', '=', 'test')\n    ->get();\n    \nforeach ($sellers as $seller){\n    echo 'Number of location = ' . $seller->locations_count;\n}\n\n```\n\n### Filter (mix left join)\n\n```\nSeller::joinRelations('city', true)\n    ->joinRelations('city.state', false)\n    ->whereJoin('city.id', '=', 1)\n    ->orWhereJoin('city.state.id', '=', 1)\n```\n\n## Generated queries\n\nQuery : \n```\nOrder::whereJoin('seller.id', '=', 1)->get();\n```\n\nSql : \n```\nselect \"orders\".* \n    from \"orders\" \n    left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n    where \"sellers\".\"id\" = ? \n    and \"orders\".\"deleted_at\" is null \n    group by \"orders\".\"id\"\n```\n\nQuery : \n```\nOrder::orderByJoin('seller.id', '=', 1)->get();\n```\n\nSql : \n```\nselect \"orders\".*, MAX(sellers.id) as sort\n    from \"orders\" \n    left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n    where \"orders\".\"deleted_at\" is null \n    group by \"orders\".\"id\"\n    order by sort asc\n```\n\n## Elegance of package\n\nLets look how first example from documentation now looks like. This code : \n\n```\n$posts = Post::select('posts.*')\n    ->join('categories', 'categories.id', '=', 'posts.category_id')\n    ->groupBy('posts.id')\n    ->where('categories.deleted_at', '=', null)\n    ->orderBy('categories.name');\n    \nif(request()->get('date')){\n    $posts->where('date', $date)\n}\n\n$posts = $posts->get();\n```\n\nis now : \n\n```\n$posts = Post::orderByJoin('category.name');\n    \nif(request()->get('date')){\n    $posts->where('posts.date', $date)\n}\n\n$posts = $posts->get();\n```\n\nBoth snippets do the same thing.\n\n## Tests\n\nThis package is well covered with tests. If you want run tests just run **composer update** and then run tests with **\"vendor/bin/phpunit\"**\n\n## Contribution\n\nFeel free to create new issue for : \n* bug\n* notice\n* request new feature\n* question\n* clarification\n* etc...\n\n\n\nLicense\n----\n\nMIT\n\n\n**Free Software, Hell Yeah!**\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"fico7489/laravel-eloquent-join\",\n    \"description\": \"This package introduces the join magic for eloquent models and relations.\",\n    \"keywords\": [\n        \"laravel join\",\n        \"laravel eloquent join\",\n        \"laravel sort join\",\n        \"laravel where join\",\n        \"laravel join relation\"\n    ],\n    \"homepage\": \"https://github.com/fico7489/laravel-eloquent-join\",\n    \"support\": {\n        \"issues\": \"https://github.com/fico7489/laravel-eloquent-join/issues\",\n        \"source\": \"https://github.com/fico7489/laravel-eloquent-join\"\n    },\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Filip Horvat\",\n            \"email\": \"filip.horvat@am2studio.hr\",\n            \"homepage\": \"http://am2studio.hr\",\n            \"role\": \"Developer\"\n        }\n    ],\n    \"require\": {\n        \"illuminate/database\": \"^8.0|^9.0|^10.0|^11.0|^12.0\"\n    },\n    \"require-dev\": {\n        \"orchestra/testbench\": \"*\",\n        \"friendsofphp/php-cs-fixer\" : \"*\",\n        \"phpunit/phpunit\": \"*\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Fico7489\\\\Laravel\\\\EloquentJoin\\\\\": \"src\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Fico7489\\\\Laravel\\\\EloquentJoin\\\\Tests\\\\\": \"tests/\"\n        }\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true\n}\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/5.7/phpunit.xsd\"\n         backupGlobals=\"false\"\n         backupStaticAttributes=\"false\"\n         colors=\"true\"\n         convertErrorsToExceptions=\"true\"\n         convertNoticesToExceptions=\"true\"\n         convertWarningsToExceptions=\"true\"\n         processIsolation=\"false\"\n         stopOnFailure=\"false\"\n         bootstrap=\"vendor/autoload.php\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\"/>\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Remember Upload Service Testsuit\">\n            <directory>./tests/</directory>\n        </testsuite>\n    </testsuites>\n\n    <filter>\n        <whitelist>\n            <directory>./src</directory>\n            <exclude>\n                <directory>./tests</directory>\n                <directory>./vendor</directory>\n            </exclude>\n        </whitelist>\n    </filter>\n</phpunit>"
  },
  {
    "path": "src/EloquentJoinBuilder.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidAggregateMethod;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidDirection;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelation;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelationClause;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelationGlobalScope;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelationWhere;\nuse Fico7489\\Laravel\\EloquentJoin\\Relations\\BelongsToJoin;\nuse Fico7489\\Laravel\\EloquentJoin\\Relations\\HasManyJoin;\nuse Fico7489\\Laravel\\EloquentJoin\\Relations\\HasOneJoin;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Relations\\Relation;\nuse Illuminate\\Database\\Eloquent\\SoftDeletingScope;\nuse Illuminate\\Database\\Query\\JoinClause;\n\nclass EloquentJoinBuilder extends Builder\n{\n    //constants\n    const AGGREGATE_SUM = 'SUM';\n    const AGGREGATE_AVG = 'AVG';\n    const AGGREGATE_MAX = 'MAX';\n    const AGGREGATE_MIN = 'MIN';\n    const AGGREGATE_COUNT = 'COUNT';\n\n    //use table alias for join (real table name or random sha1)\n    protected $useTableAlias = false;\n\n    //appendRelationsCount\n    protected $appendRelationsCount = false;\n\n    //leftJoin\n    protected $leftJoin = true;\n\n    //aggregate method\n    protected $aggregateMethod = self::AGGREGATE_MAX;\n\n    //base builder\n    protected $baseBuilder;\n\n    //store if ->select(...) is already called on builder (we want only one groupBy())\n    protected $selected = false;\n\n    //store joined tables, we want join table only once (e.g. when you call orderByJoin more time)\n    protected $joinedTables = [];\n\n    //store clauses on relation for join\n    public $relationClauses = [];\n\n    //query methods\n    public function where($column, $operator = null, $value = null, $boolean = 'and')\n    {\n        if ($column instanceof \\Closure) {\n            $query = $this->model->newModelQuery();\n            $baseBuilderCurrent = $this->baseBuilder ? $this->baseBuilder : $this;\n            $query->baseBuilder = $baseBuilderCurrent;\n\n            $column($query);\n\n            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);\n        } else {\n            $this->query->where(...func_get_args());\n        }\n\n        return $this;\n    }\n\n    public function whereJoin($column, $operator, $value, $boolean = 'and')\n    {\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n\n        return $this->where($column, $operator, $value, $boolean);\n    }\n\n    public function orWhereJoin($column, $operator, $value)\n    {\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n\n        return $this->orWhere($column, $operator, $value);\n    }\n\n    public function whereInJoin($column, $values, $boolean = 'and', $not = false)\n    {\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n\n        return $this->whereIn($column, $values, $boolean, $not);\n    }\n\n    public function whereNotInJoin($column, $values, $boolean = 'and')\n    {\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n\n        return $this->whereNotIn($column, $values, $boolean);\n    }\n\n    public function orWhereInJoin($column, $values)\n    {\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n\n        return $this->orWhereIn($column, $values);\n    }\n\n    public function orWhereNotInJoin($column, $values)\n    {\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n\n        return $this->orWhereNotIn($column, $values);\n    }\n\n    public function orderByJoin($column, $direction = 'asc', $aggregateMethod = null)\n    {\n        $direction = strtolower($direction);\n        $this->checkDirection($direction);\n        $dotPos = strrpos($column, '.');\n\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n        $column = $query->performJoin($column);\n        if (false !== $dotPos) {\n            //order by related table field\n            $aggregateMethod = $aggregateMethod ? $aggregateMethod : $this->aggregateMethod;\n            $this->checkAggregateMethod($aggregateMethod);\n\n            $sortsCount = count($this->query->orders ?? []);\n            $sortAlias = 'sort'.(0 == $sortsCount ? '' : ($sortsCount + 1));\n\n            $grammar = \\DB::query()->getGrammar();\n            $query->selectRaw($aggregateMethod.'('.$grammar->wrap($column).') as '.$sortAlias);\n\n            return $this->orderByRaw($sortAlias.' '.$direction);\n        }\n\n        //order by base table field\n\n        return $this->orderBy($column, $direction);\n    }\n\n    /**\n     * Joining relations.\n     *\n     * @param string|array $relations\n     * @param bool|null    $leftJoin\n     *\n     * @return $this\n     *\n     * @throws InvalidRelation\n     */\n    public function joinRelations($relations, $leftJoin = null)\n    {\n        $leftJoin = null !== $leftJoin ? $leftJoin : $this->leftJoin;\n        $query = $this->baseBuilder ? $this->baseBuilder : $this;\n\n        if (is_array($relations)) {\n            foreach ($relations as $relation) {\n                $query->joinRelations($relation, $leftJoin);\n            }\n        } else {\n            $query->performJoin($relations.'.FAKE_FIELD', $leftJoin);\n        }\n\n        return $this;\n    }\n\n    //helpers methods\n    protected function performJoin($relations, $leftJoin = null)\n    {\n        //detect join method\n        $leftJoin = null !== $leftJoin ? $leftJoin : $this->leftJoin;\n        $joinMethod = $leftJoin ? 'leftJoin' : 'join';\n\n        //detect current model data\n        $relations = explode('.', $relations);\n        $column = end($relations);\n        $baseModel = $this->getModel();\n        $baseTable = $baseModel->getTable();\n        $basePrimaryKey = $baseModel->getKeyName();\n\n        $currentModel = $baseModel;\n        $currentTableAlias = $baseTable;\n\n        $relationsAccumulated = [];\n        foreach ($relations as $relation) {\n            if ($relation == $column) {\n                //last item in $relations argument is sort|where column\n                break;\n            }\n\n            /** @var Relation $relatedRelation */\n            $relatedRelation = $currentModel->$relation();\n            $relatedModel = $relatedRelation->getRelated();\n            $relatedPrimaryKey = $relatedModel->getKeyName();\n            $relatedTable = $relatedModel->getTable();\n            $relatedTableAlias = $this->useTableAlias ? sha1($relatedTable.rand()) : $relatedTable;\n\n            $relationsAccumulated[] = $relatedTableAlias;\n            $relationAccumulatedString = implode('_', $relationsAccumulated);\n\n            //relations count\n            if ($this->appendRelationsCount) {\n                $this->selectRaw('COUNT('.$relatedTableAlias.'.'.$relatedPrimaryKey.') as '.$relationAccumulatedString.'_count');\n            }\n\n            if (!in_array($relationAccumulatedString, $this->joinedTables)) {\n                $joinQuery = $relatedTable.($this->useTableAlias ? ' as '.$relatedTableAlias : '');\n                if ($relatedRelation instanceof BelongsToJoin) {\n                    $relatedKey = is_callable([$relatedRelation, 'getQualifiedForeignKeyName']) ? $relatedRelation->getQualifiedForeignKeyName() : $relatedRelation->getQualifiedForeignKey();\n                    $relatedKey = last(explode('.', $relatedKey));\n                    $ownerKey = is_callable([$relatedRelation, 'getOwnerKeyName']) ? $relatedRelation->getOwnerKeyName() : $relatedRelation->getOwnerKey();\n\n                    $this->$joinMethod($joinQuery, function ($join) use ($relatedRelation, $relatedTableAlias, $relatedKey, $currentTableAlias, $ownerKey) {\n                        $join->on($relatedTableAlias.'.'.$ownerKey, '=', $currentTableAlias.'.'.$relatedKey);\n\n                        $this->joinQuery($join, $relatedRelation, $relatedTableAlias);\n                    });\n                } elseif ($relatedRelation instanceof HasOneJoin || $relatedRelation instanceof HasManyJoin) {\n                    $relatedKey = $relatedRelation->getQualifiedForeignKeyName();\n                    $relatedKey = last(explode('.', $relatedKey));\n                    $localKey = $relatedRelation->getQualifiedParentKeyName();\n                    $localKey = last(explode('.', $localKey));\n\n                    $this->$joinMethod($joinQuery, function ($join) use ($relatedRelation, $relatedTableAlias, $relatedKey, $currentTableAlias, $localKey) {\n                        $join->on($relatedTableAlias.'.'.$relatedKey, '=', $currentTableAlias.'.'.$localKey);\n\n                        $this->joinQuery($join, $relatedRelation, $relatedTableAlias);\n                    });\n                } else {\n                    throw new InvalidRelation();\n                }\n            }\n\n            $currentModel = $relatedModel;\n            $currentTableAlias = $relatedTableAlias;\n\n            $this->joinedTables[] = implode('_', $relationsAccumulated);\n        }\n\n        if (!$this->selected && count($relations) > 1) {\n            $this->selected = true;\n            $this->selectRaw($baseTable.'.*');\n            $this->groupBy($baseTable.'.'.$basePrimaryKey);\n        }\n\n        return $currentTableAlias.'.'.$column;\n    }\n\n    protected function joinQuery($join, $relation, $relatedTableAlias)\n    {\n        /** @var Builder $relationQuery */\n        $relationBuilder = $relation->getQuery();\n\n        //apply clauses on relation\n        if (isset($relationBuilder->relationClauses)) {\n            foreach ($relationBuilder->relationClauses as $clause) {\n                foreach ($clause as $method => $params) {\n                    $this->applyClauseOnRelation($join, $method, $params, $relatedTableAlias);\n                }\n            }\n        }\n\n        //apply global SoftDeletingScope\n        foreach ($relationBuilder->scopes as $scope) {\n            if ($scope instanceof SoftDeletingScope) {\n                $this->applyClauseOnRelation($join, 'withoutTrashed', [], $relatedTableAlias);\n            } else {\n                throw new InvalidRelationGlobalScope();\n            }\n        }\n    }\n\n    private function applyClauseOnRelation(JoinClause $join, string $method, array $params, string $relatedTableAlias)\n    {\n        if (in_array($method, ['where', 'orWhere'])) {\n            try {\n                if (is_array($params[0])) {\n                    foreach ($params[0] as $k => $param) {\n                        $params[0][$relatedTableAlias.'.'.$k] = $param;\n                        unset($params[0][$k]);\n                    }\n                } elseif (is_callable($params[0])) {\n                    throw new InvalidRelationWhere();\n                } else {\n                    $params[0] = $relatedTableAlias.'.'.$params[0];\n                }\n\n                call_user_func_array([$join, $method], $params);\n            } catch (\\Exception $e) {\n                throw new InvalidRelationWhere();\n            }\n        } elseif (in_array($method, ['withoutTrashed', 'onlyTrashed', 'withTrashed'])) {\n            if ('withTrashed' == $method) {\n                //do nothing\n            } elseif ('withoutTrashed' == $method) {\n                call_user_func_array([$join, 'where'], [$relatedTableAlias.'.deleted_at', '=', null]);\n            } elseif ('onlyTrashed' == $method) {\n                call_user_func_array([$join, 'where'], [$relatedTableAlias.'.deleted_at', '<>', null]);\n            }\n        } else {\n            throw new InvalidRelationClause();\n        }\n    }\n\n    private function checkAggregateMethod($aggregateMethod)\n    {\n        if (!in_array($aggregateMethod, [\n            self::AGGREGATE_SUM,\n            self::AGGREGATE_AVG,\n            self::AGGREGATE_MAX,\n            self::AGGREGATE_MIN,\n            self::AGGREGATE_COUNT,\n        ])) {\n            throw new InvalidAggregateMethod();\n        }\n    }\n\n    private function checkDirection($direction)\n    {\n        if (!in_array($direction, ['asc', 'desc'], true)) {\n            throw new InvalidDirection();\n        }\n    }\n\n    //getters and setters\n    public function isUseTableAlias(): bool\n    {\n        return $this->useTableAlias;\n    }\n\n    public function setUseTableAlias(bool $useTableAlias)\n    {\n        $this->useTableAlias = $useTableAlias;\n\n        return $this;\n    }\n\n    public function isLeftJoin(): bool\n    {\n        return $this->leftJoin;\n    }\n\n    public function setLeftJoin(bool $leftJoin)\n    {\n        $this->leftJoin = $leftJoin;\n\n        return $this;\n    }\n\n    public function isAppendRelationsCount(): bool\n    {\n        return $this->appendRelationsCount;\n    }\n\n    public function setAppendRelationsCount(bool $appendRelationsCount)\n    {\n        $this->appendRelationsCount = $appendRelationsCount;\n\n        return $this;\n    }\n\n    public function getAggregateMethod(): string\n    {\n        return $this->aggregateMethod;\n    }\n\n    public function setAggregateMethod(string $aggregateMethod)\n    {\n        $this->checkAggregateMethod($aggregateMethod);\n        $this->aggregateMethod = $aggregateMethod;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidAggregateMethod.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Exceptions;\n\nclass InvalidAggregateMethod extends \\Exception\n{\n    public $message = 'Invalid aggregate method';\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidDirection.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Exceptions;\n\nclass InvalidDirection extends \\Exception\n{\n    public $message = 'Invalid direction. Order direction must be either \\'asc\\' or \\'desc\\'';\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidRelation.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Exceptions;\n\nclass InvalidRelation extends \\Exception\n{\n    public $message = 'Package allows only following relations : BelongsTo, HasOne and HasMany.';\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidRelationClause.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Exceptions;\n\nclass InvalidRelationClause extends \\Exception\n{\n    public $message = 'Package allows only following clauses on relation : where, orWhere, withTrashed, onlyTrashed and withoutTrashed.';\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidRelationGlobalScope.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Exceptions;\n\nclass InvalidRelationGlobalScope extends \\Exception\n{\n    public $message = 'Package allows only SoftDeletingScope global scope.';\n}\n"
  },
  {
    "path": "src/Exceptions/InvalidRelationWhere.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Exceptions;\n\nclass InvalidRelationWhere extends \\Exception\n{\n    public $message = 'Package allows only following where(orWhere) clauses type on relation : ->where($column, $operator, $value) and ->where([$column => $value]).';\n}\n"
  },
  {
    "path": "src/Relations/BelongsToJoin.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Relations;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Traits\\JoinRelationTrait;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\n\nclass BelongsToJoin extends BelongsTo\n{\n    use JoinRelationTrait;\n}\n"
  },
  {
    "path": "src/Relations/HasManyJoin.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Relations;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Traits\\JoinRelationTrait;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\n\nclass HasManyJoin extends HasMany\n{\n    use JoinRelationTrait;\n}\n"
  },
  {
    "path": "src/Relations/HasOneJoin.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Relations;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Traits\\JoinRelationTrait;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\n\nclass HasOneJoin extends HasOne\n{\n    use JoinRelationTrait;\n}\n"
  },
  {
    "path": "src/Traits/EloquentJoin.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Traits;\n\nuse Fico7489\\Laravel\\EloquentJoin\\EloquentJoinBuilder;\n\n/**\n * Trait EloquentJoin.\n *\n * @method static EloquentJoinBuilder joinRelations($relations, $leftJoin = null)\n * @method static EloquentJoinBuilder whereJoin($column, $operator, $value, $boolean = 'and')\n * @method static EloquentJoinBuilder orWhereJoin($column, $operator, $value)\n * @method static EloquentJoinBuilder whereInJoin($column, $values, $boolean = 'and', $not = false)\n * @method static EloquentJoinBuilder whereNotInJoin($column, $values, $boolean = 'and')\n * @method static EloquentJoinBuilder orWhereInJoin($column, $values)\n * @method static EloquentJoinBuilder orWhereNotInJoin($column, $values)\n * @method static EloquentJoinBuilder orderByJoin($column, $direction = 'asc', $aggregateMethod = null)\n */\ntrait EloquentJoin\n{\n    use ExtendRelationsTrait;\n\n    /**\n     * @param $query\n     *\n     * @return EloquentJoinBuilder\n     */\n    public function newEloquentBuilder($query)\n    {\n        $newEloquentBuilder = new EloquentJoinBuilder($query);\n\n        if (isset($this->useTableAlias)) {\n            $newEloquentBuilder->setUseTableAlias($this->useTableAlias);\n        }\n\n        if (isset($this->appendRelationsCount)) {\n            $newEloquentBuilder->setAppendRelationsCount($this->appendRelationsCount);\n        }\n\n        if (isset($this->leftJoin)) {\n            $newEloquentBuilder->setLeftJoin($this->leftJoin);\n        }\n\n        if (isset($this->aggregateMethod)) {\n            $newEloquentBuilder->setAggregateMethod($this->aggregateMethod);\n        }\n\n        return $newEloquentBuilder;\n    }\n}\n"
  },
  {
    "path": "src/Traits/ExtendRelationsTrait.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Traits;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Relations\\BelongsToJoin;\nuse Fico7489\\Laravel\\EloquentJoin\\Relations\\HasManyJoin;\nuse Fico7489\\Laravel\\EloquentJoin\\Relations\\HasOneJoin;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Model;\n\ntrait ExtendRelationsTrait\n{\n    protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)\n    {\n        return new BelongsToJoin($query, $child, $foreignKey, $ownerKey, $relation);\n    }\n\n    protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)\n    {\n        return new HasOneJoin($query, $parent, $foreignKey, $localKey);\n    }\n\n    protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey)\n    {\n        return new HasManyJoin($query, $parent, $foreignKey, $localKey);\n    }\n}\n"
  },
  {
    "path": "src/Traits/JoinRelationTrait.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Traits;\n\nuse Fico7489\\Laravel\\EloquentJoin\\EloquentJoinBuilder;\n\ntrait JoinRelationTrait\n{\n    /**\n     * Handle dynamic method calls to the relationship.\n     *\n     * @param string $method\n     * @param array  $parameters\n     *\n     * @return mixed\n     */\n    public function __call($method, $parameters)\n    {\n        if ($this->getQuery() instanceof EloquentJoinBuilder) {\n            $this->getQuery()->relationClauses[] = [$method => $parameters];\n        }\n\n        return parent::__call($method, $parameters);\n    }\n}\n"
  },
  {
    "path": "tests/Models/BaseModel.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Traits\\EloquentJoin;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass BaseModel extends Model\n{\n    use EloquentJoin;\n}\n"
  },
  {
    "path": "tests/Models/City.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass City extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'cities';\n\n    protected $fillable = ['name'];\n\n    public function state()\n    {\n        return $this->belongsTo(State::class);\n    }\n\n    public function zipCodePrimary()\n    {\n        return $this->hasOne(ZipCode::class)\n            ->where('is_primary', '=', 1);\n    }\n\n    public function sellers()\n    {\n        return $this->belongsToMany(Seller::class, 'locations', 'seller_id', 'city_id');\n    }\n\n    public function zipCodes()\n    {\n        return $this->hasMany(ZipCode::class);\n    }\n}\n"
  },
  {
    "path": "tests/Models/Integration.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass Integration extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'integrations';\n\n    protected $fillable = ['name'];\n}\n"
  },
  {
    "path": "tests/Models/Key/Location.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\BaseModel;\n\nclass Location extends BaseModel\n{\n    protected $primaryKey = 'id_location_primary';\n\n    protected $table = 'key_locations';\n\n    protected $fillable = ['address', 'id_seller_foreign'];\n}\n"
  },
  {
    "path": "tests/Models/Key/Order.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\BaseModel;\n\nclass Order extends BaseModel\n{\n    protected $primaryKey = 'id_order_primary';\n\n    protected $table = 'key_orders';\n\n    protected $fillable = ['number', 'id_seller_foreign'];\n\n    public function seller()\n    {\n        return $this->belongsTo(Seller::class, 'id_seller_foreign', 'id_seller_primary');\n    }\n\n    public function sellerOwner()\n    {\n        return $this->belongsTo(Seller::class, 'id_seller_foreign', 'id_seller_owner');\n    }\n}\n"
  },
  {
    "path": "tests/Models/Key/Seller.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\BaseModel;\n\nclass Seller extends BaseModel\n{\n    protected $primaryKey = 'id_seller_primary';\n\n    protected $table = 'key_sellers';\n\n    protected $fillable = ['title'];\n\n    public function location()\n    {\n        return $this->hasOne(Location::class, 'id_seller_foreign', 'id_seller_primary');\n    }\n\n    public function locations()\n    {\n        return $this->hasMany(Location::class, 'id_seller_foreign', 'id_seller_primary');\n    }\n\n    public function locationOwner()\n    {\n        return $this->hasOne(Location::class, 'id_seller_foreign', 'id_seller_owner');\n    }\n\n    public function locationsOwner()\n    {\n        return $this->hasMany(Location::class, 'id_seller_foreign', 'id_seller_owner');\n    }\n}\n"
  },
  {
    "path": "tests/Models/Location.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass Location extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'locations';\n\n    protected $fillable = ['address', 'seller_id', 'is_primary', 'is_secondary', 'city_id'];\n\n    public function seller()\n    {\n        return $this->belongsTo(Seller::class);\n    }\n\n    public function city()\n    {\n        return $this->belongsTo(City::class);\n    }\n\n    public function locationAddressPrimary()\n    {\n        return $this->hasOne(LocationAddress::class)\n            ->where('is_primary', '=', 1);\n    }\n\n    public function integrations()\n    {\n        return $this->hasMany(Integration::class);\n    }\n}\n"
  },
  {
    "path": "tests/Models/LocationAddress.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass LocationAddress extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'location_addresses';\n\n    protected $fillable = ['address', 'is_primary'];\n\n    public function users()\n    {\n        return $this->hasMany(User::class);\n    }\n}\n"
  },
  {
    "path": "tests/Models/LocationWithGlobalScope.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Scope\\TestExceptionScope;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass LocationWithGlobalScope extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'locations';\n\n    protected static function boot()\n    {\n        parent::boot();\n\n        static::addGlobalScope(new TestExceptionScope());\n    }\n}\n"
  },
  {
    "path": "tests/Models/Order.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass Order extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'orders';\n\n    protected $fillable = ['number', 'seller_id'];\n\n    public function seller()\n    {\n        return $this->belongsTo(Seller::class);\n    }\n}\n"
  },
  {
    "path": "tests/Models/OrderItem.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass OrderItem extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'order_items';\n\n    protected $fillable = ['name', 'order_id'];\n\n    public function order()\n    {\n        return $this->belongsTo(Order::class);\n    }\n\n    public function orderWithTrashed()\n    {\n        return $this->belongsTo(Order::class, 'order_id')\n            ->withTrashed();\n    }\n\n    public function orderOnlyTrashed()\n    {\n        return $this->belongsTo(Order::class, 'order_id')\n            ->onlyTrashed();\n    }\n}\n"
  },
  {
    "path": "tests/Models/Seller.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nclass Seller extends BaseModel\n{\n    protected $table = 'sellers';\n\n    protected $fillable = ['title', 'deleted_at'];\n\n    public function location()\n    {\n        return $this->hasOne(Location::class)\n            ->where('is_primary', '=', 0)\n            ->where('is_secondary', '=', 0);\n    }\n\n    public function locations()\n    {\n        return $this->hasMany(Location::class);\n    }\n\n    public function locationPrimary()\n    {\n        return $this->hasOne(Location::class)\n            ->where('is_primary', '=', 1);\n    }\n\n    public function locationPrimaryInvalid()\n    {\n        return $this->hasOne(Location::class)\n            ->where('is_primary', '=', 1)\n            ->orderBy('is_primary');\n    }\n\n    public function locationPrimaryInvalid2()\n    {\n        return $this->hasOne(Location::class)\n            ->where(function ($query) {\n                return $query->where(['id' => 1]);\n            });\n    }\n\n    public function locationPrimaryInvalid3()\n    {\n        return $this->hasOne(LocationWithGlobalScope::class);\n    }\n\n    public function locationSecondary()\n    {\n        return $this->hasOne(Location::class)\n            ->where('is_secondary', '=', 1);\n    }\n\n    public function locationPrimaryOrSecondary()\n    {\n        return $this->hasOne(Location::class)\n            ->where('is_primary', '=', 1)\n            ->orWhere('is_secondary', '=', 1);\n    }\n\n    public function city()\n    {\n        return $this->belongsTo(City::class);\n    }\n}\n"
  },
  {
    "path": "tests/Models/State.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass State extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'states';\n\n    protected $fillable = ['name'];\n\n    public function cities()\n    {\n        return $this->hasMany(City::class);\n    }\n}\n"
  },
  {
    "path": "tests/Models/User.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass User extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'users';\n\n    protected $fillable = ['name'];\n}\n"
  },
  {
    "path": "tests/Models/ZipCode.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Models;\n\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\n\nclass ZipCode extends BaseModel\n{\n    use SoftDeletes;\n\n    protected $table = 'zip_codes';\n\n    protected $fillable = ['name', 'is_primary'];\n}\n"
  },
  {
    "path": "tests/Scope/TestExceptionScope.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Scope;\n\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Scope;\n\nclass TestExceptionScope implements Scope\n{\n    public function apply(Builder $builder, Model $model)\n    {\n        $builder->where('test', '=', 'test');\n    }\n}\n"
  },
  {
    "path": "tests/ServiceProvider.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests;\n\nclass ServiceProvider extends \\Illuminate\\Support\\ServiceProvider\n{\n    public function register()\n    {\n        //register\n    }\n\n    public function boot()\n    {\n        $this->loadMigrationsFrom(__DIR__.'/database/migrations/');\n    }\n\n    protected function loadMigrationsFrom($path)\n    {\n        $_ENV['type'] = 'sqlite';  //sqlite, mysql, pgsql\n\n        \\Artisan::call('migrate', ['--database' => $_ENV['type']]);\n\n        $migrator = $this->app->make('migrator');\n        $migrator->run($path);\n    }\n}\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Location;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\OrderItem;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Seller;\n\nabstract class TestCase extends \\Orchestra\\Testbench\\TestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $seller = Seller::create(['title' => 1]);\n        $seller2 = Seller::create(['title' => 2]);\n        $seller3 = Seller::create(['title' => 3]);\n        $seller4 = Seller::create(['title' => 4]);\n\n        Location::create(['address' => 1, 'seller_id' => $seller->id]);\n        Location::create(['address' => 2, 'seller_id' => $seller2->id]);\n        Location::create(['address' => 3, 'seller_id' => $seller3->id]);\n        Location::create(['address' => 3, 'seller_id' => $seller3->id]);\n\n        Location::create(['address' => 4, 'seller_id' => $seller3->id, 'is_primary' => 1]);\n        Location::create(['address' => 5, 'seller_id' => $seller3->id, 'is_secondary' => 1]);\n\n        $order = Order::create(['number' => '1', 'seller_id' => $seller->id]);\n        $order2 = Order::create(['number' => '2', 'seller_id' => $seller2->id]);\n        $order3 = Order::create(['number' => '3', 'seller_id' => $seller3->id]);\n\n        OrderItem::create(['name' => '1', 'order_id' => $order->id]);\n        OrderItem::create(['name' => '2', 'order_id' => $order2->id]);\n        OrderItem::create(['name' => '3', 'order_id' => $order3->id]);\n\n        $this->startListening();\n    }\n\n    protected function startListening()\n    {\n        \\DB::enableQueryLog();\n    }\n\n    protected function fetchLastLog()\n    {\n        $log = \\DB::getQueryLog();\n\n        return end($log);\n    }\n\n    protected function fetchQuery()\n    {\n        $query = $this->fetchLastLog()['query'];\n        $bindings = $this->fetchLastLog()['bindings'];\n\n        foreach ($bindings as $binding) {\n            $binding = is_string($binding) ? ('\"'.$binding.'\"') : $binding;\n            $query = preg_replace('/\\?/', $binding, $query, 1);\n        }\n\n        return $query;\n    }\n\n    protected function fetchBindings()\n    {\n        return $this->fetchLastLog()['bindings'];\n    }\n\n    protected function getEnvironmentSetUp($app)\n    {\n        // Setup default database to use sqlite :memory:\n        $app['config']->set('database.connections.sqlite', [\n            'driver' => 'sqlite',\n            'database' => ':memory:',\n            'prefix' => '',\n        ]);\n\n        $app['config']->set('database.connections.mysql', [\n            'driver' => 'mysql',\n            'host' => 'localhost',\n            'database' => 'join',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n            'collation' => 'utf8_unicode_ci',\n            'strict' => true,\n        ]);\n\n        $app['config']->set('database.connections.pgsql', [\n            'driver' => 'pgsql',\n            'host' => 'localhost',\n            'database' => 'join',\n            'username' => 'postgres',\n            'password' => 'root',\n            'charset' => 'utf8',\n            'prefix' => '',\n            'schema' => 'public',\n            'sslmode' => 'prefer',\n        ]);\n\n        $app['config']->set('database.default', env('type', 'sqlite'));\n    }\n\n    protected function getPackageProviders($app)\n    {\n        return [ServiceProvider::class];\n    }\n\n    protected function assertQueryMatches($expected, $actual)\n    {\n        $actual = preg_replace('/\\s\\s+/', ' ', $actual);\n        $actual = str_replace(['\\n', '\\r'], '', $actual);\n\n        $expected = preg_replace('/\\s\\s+/', ' ', $expected);\n        $expected = str_replace(['\\n', '\\r'], '', $expected);\n        $expected = '/'.$expected.'/';\n        $expected = preg_quote($expected);\n        if ('mysql' == $_ENV['type']) {\n            $expected = str_replace(['\"'], '`', $expected);\n        }\n\n        $this->assertMatchesRegularExpression($expected, $actual);\n    }\n}\n"
  },
  {
    "path": "tests/Tests/AggregateJoinTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\EloquentJoinBuilder;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass AggregateJoinTest extends TestCase\n{\n    private $queryTest = 'select orders.*, SUM(\"sellers\".\"id\") as sort \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\" \n            order by sort asc';\n\n    public function testAvg()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc', EloquentJoinBuilder::AGGREGATE_SUM)\n            ->get();\n\n        $queryTest = str_replace(EloquentJoinBuilder::AGGREGATE_SUM, EloquentJoinBuilder::AGGREGATE_SUM, $this->queryTest);\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testSum()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc', EloquentJoinBuilder::AGGREGATE_AVG)\n            ->get();\n\n        $queryTest = str_replace(EloquentJoinBuilder::AGGREGATE_SUM, EloquentJoinBuilder::AGGREGATE_AVG, $this->queryTest);\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testMax()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc', EloquentJoinBuilder::AGGREGATE_MAX)\n            ->get();\n\n        $queryTest = str_replace(EloquentJoinBuilder::AGGREGATE_SUM, EloquentJoinBuilder::AGGREGATE_MAX, $this->queryTest);\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testMin()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc', EloquentJoinBuilder::AGGREGATE_MIN)\n            ->get();\n\n        $queryTest = str_replace(EloquentJoinBuilder::AGGREGATE_SUM, EloquentJoinBuilder::AGGREGATE_MIN, $this->queryTest);\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testCount()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc', EloquentJoinBuilder::AGGREGATE_COUNT)\n            ->get();\n\n        $queryTest = str_replace(EloquentJoinBuilder::AGGREGATE_SUM, EloquentJoinBuilder::AGGREGATE_COUNT, $this->queryTest);\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/AppendRelationsCountTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass AppendRelationsCountTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::setAppendRelationsCount(true)->joinRelations('seller.locationPrimary.locationAddressPrimary')->get();\n\n        $queryTest = 'select COUNT(sellers.id) as sellers_count, COUNT(locations.id) as sellers_locations_count, COUNT(location_addresses.id) as sellers_locations_location_addresses_count, orders.*\n            from \"orders\"\n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            left join \"location_addresses\" on \"location_addresses\".\"location_id\" = \"locations\".\"id\" \n            and \"location_addresses\".\"is_primary\" = 1 \n            and \"location_addresses\".\"deleted_at\" is null \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/JoinRelationsTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass JoinRelationsTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/OrWhereInTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass OrWhereInTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->whereInJoin('seller.id', [1, 2])\n            ->orWhereInJoin('seller.id', [3, 4])\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where (\"sellers\".\"id\" in (1, 2) \n                or \n                \"sellers\".\"id\" in (3, 4))\n            and \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/OrWhereNotInTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass OrWhereNotInTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->whereInJoin('seller.id', [1, 2])\n            ->orWhereNotInJoin('seller.id', [3, 4])\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where (\"sellers\".\"id\" in (1, 2) \n                or \n                \"sellers\".\"id\" not in (3, 4))\n            and \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/OrWhereTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass OrWhereTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->whereJoin('seller.id', '=', 1)\n            ->orWhereJoin('seller.id', '=', 2)\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where (\"sellers\".\"id\" = 1 or \"sellers\".\"id\" = 2) \n            and \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/OrderByTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass OrderByTest extends TestCase\n{\n    public function testOrderBy()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc')\n            ->get();\n\n        $queryTest = 'select orders.*, MAX(\"sellers\".\"id\") as sort\n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testOrderByMultiple()\n    {\n        Order::joinRelations('seller')\n            ->orderByJoin('seller.id', 'asc')\n            ->orderByJoin('seller.title', 'desc')\n            ->get();\n\n        $queryTest = 'select orders.*, MAX(\"sellers\".\"id\") as sort, MAX(\"sellers\".\"title\") as sort2\n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"\n            order by sort asc, sort2 desc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/WhereInTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass WhereInTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->whereInJoin('seller.id', [1, 2])\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"sellers\".\"id\" in (1, 2)\n            and \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/WhereNotInTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass WhereNotInTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->whereNotInJoin('seller.id', [1, 2])\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"sellers\".\"id\" not in (1, 2)\n            and \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Clauses/WhereTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Clauses;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass WhereTest extends TestCase\n{\n    public function testWhere()\n    {\n        Order::joinRelations('seller')\n            ->whereJoin('seller.id', '=', 1)\n            ->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"sellers\".\"id\" = 1 \n            and \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ClosureOnRelationTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass ClosureOnRelationTest extends TestCase\n{\n    public function testWhereOnRelationWithOrderByJoin()\n    {\n        //location have two where  ['is_primary => 0', 'is_secondary' => 0]\n        $items = Seller::orderByJoin('location.id', 'desc')->get();\n        $queryTest = 'select sellers.*, MAX(\"locations\".\"id\") as sort from \"sellers\" \n            left join \"locations\" \n            on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 0 \n            and \"locations\".\"is_secondary\" = 0 \n            and \"locations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"\n            order by sort desc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n\n        //locationPrimary have one where ['is_primary => 1']\n        $items = Seller::orderByJoin('locationPrimary.id', 'desc')->get();\n        $queryTest = 'select sellers.*, MAX(\"locations\".\"id\") as sort from \"sellers\" \n            left join \"locations\" \n            on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"\n            order by sort desc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n\n        //locationPrimary have one where ['is_secondary => 1']\n        $items = Seller::orderByJoin('locationSecondary.id', 'desc')->get();\n        $queryTest = 'select sellers.*, MAX(\"locations\".\"id\") as sort from \"sellers\" \n            left join \"locations\" \n            on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_secondary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"\n            order by sort desc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n\n        //locationPrimary have one where ['is_primary => 1'] and one orWhere ['is_secondary => 1']\n        $items = Seller::orderByJoin('locationPrimaryOrSecondary.id', 'desc')->get();\n        $queryTest = 'select sellers.*, MAX(\"locations\".\"id\") as sort from \"sellers\" \n            left join \"locations\" \n            on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 \n            or \"locations\".\"is_secondary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"\n            order by sort desc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testWhereOnRelationWithoutOrderByJoin()\n    {\n        $seller = Seller::find(1);\n\n        $seller->locationPrimary;\n        $queryTest = 'select * from \"locations\" \n            where \"locations\".\"seller_id\" = 1 \n            and \"locations\".\"seller_id\" is not null \n            and \"is_primary\" = 1\n            and \"locations\".\"deleted_at\" is null \n            limit 1';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n\n        $seller->locationPrimary()->where(['is_secondary' => 1])->get();\n        $queryTest = 'select * from \"locations\" \n            where \"locations\".\"seller_id\" = 1 \n            and \"locations\".\"seller_id\" is not null \n            and \"is_primary\" = 1\n            and (\"is_secondary\" = 1)\n            and \"locations\".\"deleted_at\" is null';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ClosureTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\OrderItem;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass ClosureTest extends TestCase\n{\n    public function testNestOne()\n    {\n        OrderItem::where(function ($query) {\n            $query\n                ->orWhereJoin('order.id', '=', 1)\n                ->orWhereJoin('order.id', '=', 2);\n        })->get();\n\n        $queryTest = 'select order_items.* \n            from \"order_items\" \n            left join \"orders\" on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            and \"orders\".\"deleted_at\" is null \n            where (\"orders\".\"id\" = 1 or \"orders\".\"id\" = 2) \n            and \"order_items\".\"deleted_at\" is null';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testNestTwo()\n    {\n        OrderItem::where(function ($query) {\n            $query\n                ->orWhereJoin('order.id', '=', 1)\n                ->orWhereJoin('order.id', '=', 2)\n                ->where(function ($query) {\n                    $query->orWhereJoin('order.seller.locationPrimary.id', '=', 3);\n                });\n        })->get();\n\n        $queryTest = 'select order_items.* \n            from \"order_items\" \n            left join \"orders\" on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            and \"orders\".\"deleted_at\" is null \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            where (\"orders\".\"id\" = 1 or \"orders\".\"id\" = 2 \n            and (\"locations\".\"id\" = 3)) \n            and \"order_items\".\"deleted_at\" is null';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/ExceptionTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidAggregateMethod;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidDirection;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelation;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelationClause;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelationGlobalScope;\nuse Fico7489\\Laravel\\EloquentJoin\\Exceptions\\InvalidRelationWhere;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\City;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass ExceptionTest extends TestCase\n{\n    public function testInvalidRelation()\n    {\n        try {\n            City::whereJoin('sellers.id', '=', 'test')->get();\n        } catch (InvalidRelation $e) {\n            $this->assertEquals((new InvalidRelation())->message, $e->getMessage());\n\n            return;\n        }\n\n        $this->assertTrue(false);\n    }\n\n    public function testInvalidRelationWhere()\n    {\n        try {\n            Seller::whereJoin('locationPrimaryInvalid2.name', '=', 'test')->get();\n        } catch (InvalidRelationWhere $e) {\n            $this->assertEquals((new InvalidRelationWhere())->message, $e->getMessage());\n\n            return;\n        }\n\n        $this->assertTrue(false);\n    }\n\n    public function testInvalidRelationClause()\n    {\n        try {\n            Seller::whereJoin('locationPrimaryInvalid.name', '=', 'test')->get();\n        } catch (InvalidRelationClause $e) {\n            $this->assertEquals((new InvalidRelationClause())->message, $e->getMessage());\n\n            return;\n        }\n\n        $this->assertTrue(false);\n    }\n\n    public function testInvalidRelationGlobalScope()\n    {\n        try {\n            Seller::whereJoin('locationPrimaryInvalid3.id', '=', 'test')->get();\n        } catch (InvalidRelationGlobalScope $e) {\n            $this->assertEquals((new InvalidRelationGlobalScope())->message, $e->getMessage());\n\n            return;\n        }\n\n        $this->assertTrue(false);\n    }\n\n    public function testInvalidAggregateMethod()\n    {\n        try {\n            Seller::orderByJoin('locationPrimary.id', 'asc', 'wrong')->get();\n        } catch (InvalidAggregateMethod $e) {\n            $this->assertEquals((new InvalidAggregateMethod())->message, $e->getMessage());\n\n            return;\n        }\n\n        $this->assertTrue(false);\n    }\n\n    public function testOrderByInvalidDirection()\n    {\n        $this->expectException(InvalidDirection::class);\n        Seller::orderByJoin('locationPrimary.id', ';DROP TABLE orders;--', 'test')->get();\n    }\n}\n"
  },
  {
    "path": "tests/Tests/JoinTypeTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass JoinTypeTest extends TestCase\n{\n    public function testLeftJoin()\n    {\n        Seller::setLeftJoin(true)->whereJoin('city.name', '=', 'test')->get();\n\n        $queryTest = 'select sellers.*\n            from \"sellers\"\n            left join \"cities\"\n            on \"cities\".\"id\" = \"sellers\".\"city_id\"\n            and \"cities\".\"deleted_at\" is null\n            where \"cities\".\"name\" = \"test\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testInnerJoin()\n    {\n        Seller::setLeftJoin(false)->whereJoin('city.name', '=', 'test')->get();\n\n        $queryTest = 'select sellers.*\n            from \"sellers\"\n            inner join \"cities\"\n            on \"cities\".\"id\" = \"sellers\".\"city_id\"\n            and \"cities\".\"deleted_at\" is null\n            where \"cities\".\"name\" = \"test\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testMixedJoin()\n    {\n        Order::joinRelations('seller', true)->joinRelations('seller.city', false)->joinRelations('seller.city.state', true)->get();\n\n        $queryTest = 'select orders.* \n            from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            inner join \"cities\" on \"cities\".\"id\" = \"sellers\".\"city_id\" \n            and \"cities\".\"deleted_at\" is null \n            left join \"states\" on \"states\".\"id\" = \"cities\".\"state_id\" \n            and \"states\".\"deleted_at\" is null \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/KeysOwnerTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass KeysOwnerTest extends TestCase\n{\n    public function testBelogsTo()\n    {\n        Order::joinRelations('sellerOwner')\n            ->get();\n\n        $queryTest = 'select key_orders.* \n            from \"key_orders\" \n            left join \"key_sellers\" on \"key_sellers\".\"id_seller_owner\" = \"key_orders\".\"id_seller_foreign\" \n            group by \"key_orders\".\"id_order_primary\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasOne()\n    {\n        Seller::joinRelations('locationOwner')\n            ->get();\n\n        $queryTest = 'select key_sellers.* \n            from \"key_sellers\" \n            left join \"key_locations\" on \"key_locations\".\"id_seller_foreign\" = \"key_sellers\".\"id_seller_owner\" \n            group by \"key_sellers\".\"id_seller_primary\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasMany()\n    {\n        Seller::joinRelations('locationsOwner')\n            ->get();\n\n        $queryTest = 'select key_sellers.* \n            from \"key_sellers\" \n            left join \"key_locations\" on \"key_locations\".\"id_seller_foreign\" = \"key_sellers\".\"id_seller_owner\" \n            group by \"key_sellers\".\"id_seller_primary\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/KeysTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Key\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass KeysTest extends TestCase\n{\n    public function testBelongsTo()\n    {\n        Order::joinRelations('seller')\n            ->get();\n\n        $queryTest = 'select key_orders.* \n            from \"key_orders\" \n            left join \"key_sellers\" on \"key_sellers\".\"id_seller_primary\" = \"key_orders\".\"id_seller_foreign\" \n            group by \"key_orders\".\"id_order_primary\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasOne()\n    {\n        Seller::joinRelations('location')\n            ->get();\n\n        $queryTest = 'select key_sellers.*\n            from \"key_sellers\"\n            left join \"key_locations\" on \"key_locations\".\"id_seller_foreign\" = \"key_sellers\".\"id_seller_primary\"\n            group by \"key_sellers\".\"id_seller_primary\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasMany()\n    {\n        Seller::joinRelations('locations')\n            ->get();\n\n        $queryTest = 'select key_sellers.*\n            from \"key_sellers\"\n            left join \"key_locations\" on \"key_locations\".\"id_seller_foreign\" = \"key_sellers\".\"id_seller_primary\"\n            group by \"key_sellers\".\"id_seller_primary\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/OptionsTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\EloquentJoinBuilder;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\City;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass OptionsTest extends TestCase\n{\n    public function testUseTableAlias()\n    {\n        $city = new City();\n        $this->assertEquals(false, $city->newModelQuery()->isUseTableAlias());\n        $city->useTableAlias = true;\n        $this->assertEquals(true, $city->newModelQuery()->isUseTableAlias());\n    }\n\n    public function testAppendRelationsCount()\n    {\n        $city = new City();\n        $this->assertEquals(false, $city->newModelQuery()->isAppendRelationsCount());\n        $city->appendRelationsCount = true;\n        $this->assertEquals(true, $city->newModelQuery()->isAppendRelationsCount());\n    }\n\n    public function testLeftJoin()\n    {\n        $city = new City();\n        $this->assertEquals(true, $city->newModelQuery()->isLeftJoin());\n        $city->leftJoin = false;\n        $this->assertEquals(false, $city->newModelQuery()->isLeftJoin());\n    }\n\n    public function testAggregateMethod()\n    {\n        $city = new City();\n        $this->assertEquals(EloquentJoinBuilder::AGGREGATE_MAX, $city->newModelQuery()->getAggregateMethod());\n        $city->aggregateMethod = EloquentJoinBuilder::AGGREGATE_MIN;\n        $this->assertEquals(EloquentJoinBuilder::AGGREGATE_MIN, $city->newModelQuery()->getAggregateMethod());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Relations/BelongsToTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Relations;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Order;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass BelongsToTest extends TestCase\n{\n    public function testBelongsTo()\n    {\n        Order::joinRelations('seller')->get();\n\n        $queryTest = 'select orders.* from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testBelongsToHasOne()\n    {\n        Order::joinRelations('seller.locationPrimary')->get();\n\n        $queryTest = 'select orders.* from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            where \"orders\".\"deleted_at\" is null\n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testBelongsToHasMany()\n    {\n        Order::joinRelations('seller.locations')->get();\n\n        $queryTest = 'select orders.* from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"deleted_at\" is null \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testBelongsToHasOneHasMany()\n    {\n        Order::joinRelations('seller.locationPrimary.integrations')->get();\n\n        $queryTest = 'select orders.* from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 and \"locations\".\"deleted_at\" is null \n            left join \"integrations\" on \"integrations\".\"location_id\" = \"locations\".\"id\" \n            and \"integrations\".\"deleted_at\" is null \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testBelongsToHasManyHasOne()\n    {\n        Order::joinRelations('seller.locationPrimary.locationAddressPrimary')->get();\n\n        $queryTest = 'select orders.* from \"orders\" \n            left join \"sellers\" on \"sellers\".\"id\" = \"orders\".\"seller_id\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 1 \n            and \"locations\".\"deleted_at\" is null \n            left join \"location_addresses\" on \"location_addresses\".\"location_id\" = \"locations\".\"id\" \n            and \"location_addresses\".\"is_primary\" = 1 \n            and \"location_addresses\".\"deleted_at\" is null \n            where \"orders\".\"deleted_at\" is null \n            group by \"orders\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Relations/HasManyTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Relations;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass HasManyTest extends TestCase\n{\n    public function testHasMany()\n    {\n        Seller::joinRelations('locations')->get();\n\n        $queryTest = 'select sellers.* \n            from \"sellers\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasManyHasOne()\n    {\n        Seller::joinRelations('locations.city')->get();\n\n        $queryTest = 'select sellers.* \n            from \"sellers\" left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"deleted_at\" is null \n            left join \"cities\" on \"cities\".\"id\" = \"locations\".\"city_id\" \n            and \"cities\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasManyBelongsTo()\n    {\n        Seller::joinRelations('locations.integrations')->get();\n\n        $queryTest = 'select sellers.* \n            from \"sellers\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"deleted_at\" is null \n            left join \"integrations\" on \"integrations\".\"location_id\" = \"locations\".\"id\"\n            and \"integrations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/Relations/HasOneTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests\\Relations;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\Seller;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass HasOneTest extends TestCase\n{\n    public function testHasOne()\n    {\n        Seller::joinRelations('location')->get();\n\n        $queryTest = 'select sellers.* \n            from \"sellers\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 0\n            and \"locations\".\"is_secondary\" = 0 \n            and \"locations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasOneBelongsTo()\n    {\n        Seller::joinRelations('location.city')->get();\n\n        $queryTest = 'select sellers.* \n            from \"sellers\" left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 0\n            and \"locations\".\"is_secondary\" = 0 \n            and \"locations\".\"deleted_at\" is null \n            left join \"cities\" on \"cities\".\"id\" = \"locations\".\"city_id\" \n            and \"cities\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testHasOneHasMany()\n    {\n        Seller::joinRelations('location.integrations')->get();\n\n        $queryTest = 'select sellers.* \n            from \"sellers\" \n            left join \"locations\" on \"locations\".\"seller_id\" = \"sellers\".\"id\" \n            and \"locations\".\"is_primary\" = 0\n            and \"locations\".\"is_secondary\" = 0 \n            and \"locations\".\"deleted_at\" is null \n            left join \"integrations\" on \"integrations\".\"location_id\" = \"locations\".\"id\"\n            and \"integrations\".\"deleted_at\" is null \n            group by \"sellers\".\"id\"';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/Tests/SoftDeleteTest.php",
    "content": "<?php\n\nnamespace Fico7489\\Laravel\\EloquentJoin\\Tests\\Tests;\n\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\Models\\OrderItem;\nuse Fico7489\\Laravel\\EloquentJoin\\Tests\\TestCase;\n\nclass SoftDeleteTest extends TestCase\n{\n    public function testNotRelatedWithoutTrashedDefault()\n    {\n        OrderItem::orderByJoin('name')->get();\n        $queryTest = 'select * \n            from \"order_items\" \n            where \"order_items\".\"deleted_at\" is null \n            order by \"order_items\".\"name\" asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testNotRelatedWithoutTrashedExplicit()\n    {\n        OrderItem::orderByJoin('name')->withoutTrashed()->get();\n        $queryTest = 'select * \n            from \"order_items\"\n            where \"order_items\".\"deleted_at\" is null\n            order by \"order_items\".\"name\" asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testNotRelatedOnlyTrashedExplicit()\n    {\n        OrderItem::orderByJoin('name')->onlyTrashed()->get();\n        $queryTest = 'select * \n            from \"order_items\" \n            where \"order_items\".\"deleted_at\" is not null\n            order by \"order_items\".\"name\" asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testNotRelatedWithTrashedExplicit()\n    {\n        OrderItem::orderByJoin('name')->withTrashed()->get();\n        $queryTest = 'select * \n            from \"order_items\" \n            order by \"order_items\".\"name\" asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testRelatedWithoutTrashedDefault()\n    {\n        OrderItem::orderByJoin('order.number')->get();\n        $queryTest = 'select order_items.*, MAX(\"orders\".\"number\") as sort\n            from \"order_items\" left join \"orders\" \n            on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            and \"orders\".\"deleted_at\" is null\n            where \"order_items\".\"deleted_at\" is null \n            group by \"order_items\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testRelatedWithoutTrashedExplicit()\n    {\n        OrderItem::orderByJoin('order.number')->withoutTrashed()->get();\n        $queryTest = 'select order_items.*, MAX(\"orders\".\"number\") as sort\n            from \"order_items\" \n            left join \"orders\" \n            on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            and \"orders\".\"deleted_at\" is null \n            where \"order_items\".\"deleted_at\" is null \n            group by \"order_items\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testRelatedOnlyTrashedExplicit()\n    {\n        OrderItem::orderByJoin('order.number')->onlyTrashed()->get();\n        $queryTest = 'select order_items.*, MAX(\"orders\".\"number\") as sort\n            from \"order_items\" \n            left join \"orders\" \n            on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            and \"orders\".\"deleted_at\" is null \n            where \"order_items\".\"deleted_at\" is not null\n            group by \"order_items\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testRelatedWithTrashedExplicit()\n    {\n        OrderItem::orderByJoin('order.number')->withTrashed()->get();\n        $queryTest = 'select order_items.*, MAX(\"orders\".\"number\") as sort\n            from \"order_items\" \n            left join \"orders\" \n            on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            and \"orders\".\"deleted_at\" is null \n            group by \"order_items\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testRelatedWithTrashedOnRelation()\n    {\n        OrderItem::orderByJoin('orderWithTrashed.number')->get();\n        $queryTest = 'select order_items.*, MAX(\"orders\".\"number\") as sort\n            from \"order_items\" \n            left join \"orders\" \n            on \"orders\".\"id\" = \"order_items\".\"order_id\" \n            where \"order_items\".\"deleted_at\" is null\n            group by \"order_items\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n\n    public function testRelatedOnlyTrashedOnRelation()\n    {\n        OrderItem::orderByJoin('orderOnlyTrashed.number')->get();\n        $queryTest = 'select order_items.*, MAX(\"orders\".\"number\") as sort\n            from \"order_items\"\n            left join \"orders\" \n            on \"orders\".\"id\" = \"order_items\".\"order_id\"\n            and \"orders\".\"deleted_at\" is not null \n            where \"order_items\".\"deleted_at\" is null \n            group by \"order_items\".\"id\"\n            order by sort asc';\n\n        $this->assertQueryMatches($queryTest, $this->fetchQuery());\n    }\n}\n"
  },
  {
    "path": "tests/database/migrations/2017_11_04_163552_create_database.php",
    "content": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\n\n/**\n * Class CreateDatabase.\n */\nclass CreateDatabase extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up()\n    {\n        Schema::create('states', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name')->nullable();\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('cities', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name')->nullable();\n            $table->unsignedInteger('state_id')->nullable();\n\n            $table->foreign('state_id')->references('id')->on('states');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('sellers', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('title')->nullable();\n            $table->unsignedInteger('city_id')->nullable();\n\n            $table->foreign('city_id')->references('id')->on('cities');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('orders', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('number')->nullable();\n            $table->unsignedInteger('seller_id')->nullable();\n\n            $table->foreign('seller_id')->references('id')->on('sellers');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('order_items', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name');\n            $table->unsignedInteger('order_id')->nullable();\n\n            $table->foreign('order_id')->references('id')->on('orders');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('locations', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('address')->nullable();\n            $table->boolean('is_primary')->default(0);\n            $table->boolean('is_secondary')->default(0);\n            $table->unsignedInteger('seller_id')->nullable();\n            $table->unsignedInteger('city_id')->nullable();\n\n            $table->foreign('seller_id')->references('id')->on('sellers');\n            $table->foreign('city_id')->references('id')->on('cities');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('zip_codes', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name')->nullable();\n            $table->boolean('is_primary')->default(0);\n            $table->unsignedInteger('city_id')->nullable();\n\n            $table->foreign('city_id')->references('id')->on('cities');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('location_addresses', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name')->nullable();\n            $table->boolean('is_primary')->default(0);\n            $table->unsignedInteger('location_id')->nullable();\n\n            $table->foreign('location_id')->references('id')->on('locations');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('users', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name')->nullable();\n            $table->unsignedInteger('location_address_id')->nullable();\n\n            $table->foreign('location_address_id')->references('id')->on('location_addresses');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        Schema::create('integrations', function (Blueprint $table) {\n            $table->increments('id');\n            $table->string('name')->nullable();\n            $table->unsignedInteger('location_id')->nullable();\n\n            $table->foreign('location_id')->references('id')->on('locations');\n\n            $table->timestamps();\n            $table->softDeletes();\n        });\n\n        //for key tests\n        Schema::create('key_orders', function (Blueprint $table) {\n            $table->increments('id_order_primary');\n            $table->unsignedInteger('id_order_owner')->nullable();\n\n            $table->string('number')->nullable();\n\n            $table->unsignedInteger('id_seller_foreign')->nullable();\n            $table->foreign('id_seller_foreign')->references('id')->on('sellers');\n        });\n\n        Schema::create('key_sellers', function (Blueprint $table) {\n            $table->increments('id_seller_primary');\n            $table->unsignedInteger('id_seller_owner')->nullable();\n\n            $table->string('title')->nullable();\n        });\n\n        Schema::create('key_locations', function (Blueprint $table) {\n            $table->increments('id_location_primary');\n            $table->unsignedInteger('id_location_owner')->nullable();\n\n            $table->string('address')->nullable();\n\n            $table->unsignedInteger('id_seller_foreign')->nullable();\n            $table->foreign('id_seller_foreign')->references('id')->on('sellers');\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down()\n    {\n        Schema::drop('users');\n        Schema::drop('sellers');\n        Schema::drop('order_items');\n        Schema::drop('locations');\n        Schema::drop('cities');\n        Schema::drop('zip_codes');\n        Schema::drop('states');\n        Schema::drop('location_addresses');\n        Schema::drop('integrations');\n        Schema::drop('orders');\n\n        //for key tests\n        Schema::drop('key_orders');\n        Schema::drop('key_sellers');\n        Schema::drop('key_locations');\n    }\n}\n"
  }
]