jhavenz/models-collection

a simple way to collect/iterate over the eloquent models in a project

0.0.6 2022-08-28 20:53 UTC

This package is auto-updated.

Last update: 2024-04-11 02:02:04 UTC


README

A simple way to iterate the Eloquent models in your project

(see tests for additional examples)

Basic Usage:

use App\Models\User;
use Jhavenz\ModelsCollection\ModelsCollection;

# works as you'd expect - more examples given below, and in the tests
modelsCollection()->whereInstanceOf(User::class)->first();

# functionally equivalent
ModelsCollection::usingFilters(User::class)->first(); 

You can also use multiple filters at once:

Example:

use App\Models\User;
use App\Models\Post;
use App\OtherModels\Pivot\PostUser;
use Jhavenz\ModelsCollection\ModelsCollection;

# Will create a `ModelsCollection` with an instance if each model:
ModelsCollection::usingFilters(
    User::class, 
    Post::class, 
    PostUser::class,
);

Note: The 'OtherModels' namespace (listed above) would need to be listed in your models-collection.directories configuration

Example:

// config/models-collection.php

return [
    'directories' => [
        app_path('Models'),
        app_path('OtherModels')   
    ]
];

Installation

You can install the package via composer:

composer require jhavenz/models-collection

You can publish the config file with:

php artisan vendor:publish --tag="models-collection-config"

This is the contents of the published config file:

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Model Directories
    |--------------------------------------------------------------------------
    | 
    | Path(s) to where your models live in your project (use absolute paths).
    |
    | You're welcome to use as many directories as you'd like. This package
    | will automatically filter out files/folders that aren't Eloquent
    | models.
    |
    */
    
    'directories' => [
        app_path('Models'),
        //
    ],
];

Usage

This package will scan any directory that your Eloquent models are in (using Symfony's Finder Component) then return a ModelsCollection that can then be used as a Laravel collection, including some custom helper methods.

examples given as if we have a \App\Models\User and \App\Models\Post model

use Jhavenz\ModelsCollection\ModelsCollection;

$totals = [];

/** @var \Illuminate\Database\Eloquent\Model $model */
foreach (ModelsCollection::create() as $model) {
    $totals[$model->getTable()] = $model->newQuery()->count();
}

dump($totals);
/**
   [
      'users' => 20,
      'posts' => 50,
   ]
 */

Any methods called, but not found on the ModelsCollection class, will automatically resolve the models relative to any files, directories, and/or filters you've listed within your ModelsCollection, collect these models, then call that method on an \Illuminate\Support\Collection before being returned to you.

ModelsCollection::create()->map->getTable();

dump($totals);
/**
   [
      0 => 'users',
      1 => 'posts',
   ]
 */

Depth Usage

Since this package uses the Symfony Finder component, we can apply depth filters when creating a new instance:

this example assumes there's a \App\Models\Pivot\PostUser model in addition to the example models above

Example:

use App\Models\Pivot\PostUser;
use Jhavenz\ModelsCollection\ModelsCollection;

$pivotModels = ModelsCollection::usingDepth('== 1');

$pivotModels->count(); //=> 1
$pivotModels->first(); //=> \App\Models\Pivot\PostUser instance

//or, if we want to exclude this 'pivot' model
$nonPivotModels = ModelsCollection::usingDepth('< 1');

$nonPivotModels->count(); //=> 2
$nonPivotModels->whereInstanceOf(PostUser::class)->isEmpty(); //=> true

see symfony's docs on this for more depth control options

Filter Usage

In addition to the basic class string filters used above, this package also provides the ability to filter using a custom callback.

Example:

use App\Models\Pivot\PostUser;
use Jhavenz\ModelsCollection\ModelsCollection;
use Jhavenz\ModelsCollection\Structs\Filesystem\FilePath;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;

// basic example...
$pivotModels = ModelsCollection::usingFilters(function (FilePath $filepath) {
    //$filepath is essentially a \SplFileInfo on steroids...
    return $filepath->instance() instanceof Pivot;
});

$pivotModels->count(); //=> 1
$pivotModels->first(); //=> instance of PostUser

//more advanced example
$belongsToPost = ModelsCollection::usingFilters(function (FilePath $filepath) {
    
    //e.g. find all models that 'BelongTo' to a 'user'...
    foreach ($filepath->reflectionClass()->getMethods(ReflectionMethod::IS_PUBLIC) as $rfxMethod) {
        $methodName = $rfxMethod->getName();
        $methodReturnType = $rfxMethod->getReturnType();
        
        if ($methodName === 'user' && $methodReturnType === BelongsTo::class) {
           return true;
        }
    }
    
    return false;
});

$belongsToPost->count(); //=> 2
$belongsToPost->all(); //=> [PostUser {#...}, Post {#...}]

IOC Instantiation

This package makes heavy use the Laravel container whenever calling one of the static constructors that have been provided: create or make.

It's highly recommended to use one of these method whenever creating a new ModelsCollection instance. This allows you to use the built-in power of the IoC container whenever creating a new instance.

In other words, never use new ModelsCollection(...) to create an instance.

Unless, of course, you'd like to create an instance without the container knowing about it.

Testing

composer test

Contributing

Contributions are welcome!

Security Vulnerabilities

Please email me at mail@jhavens.tech if any vulnerabilities are found

Credits

License

The MIT License (MIT). Please see License File for more information.