Laravel package for Sammyjo20/Saloon

Overview

Saloon

Saloon πŸšͺ πŸšͺ

Interact with REST APIs with confidence and elegance.

Saloon is a PHP package which introduces a class-based/OOP approach to building connections to APIs. Saloon introduces an easy to understand pattern to help you standardise the way you interact with third-party APIs, reduce repeated code (DRY) and lets you mock API requests for your tests.

Larael Package

This is the Laravel package for Saloon, to install it run the following command

composer require sammyjo20/saloon-laravel

Requires Laravel 8+ and PHP 8.0+

Docs

Click here to read the documentation

Comments
  • Feature | Record Requests In Telescope

    Feature | Record Requests In Telescope

    This PR introduces a new listener which can be subscribed to so that it records requests in Laravel Telescope. The telescope entry will be recorded under the "HTTP Client" section of Telescope.

    Fixes #21

    opened by Sammyjo20 7
  • Dependency injection improvements

    Dependency injection improvements

    Is it possible in saloon-laravel override bootConnector method so its resolve dependency from laravel service container?

    Problem is when writing tests cannot manage to mock it because of this line: https://github.com/Sammyjo20/Saloon/blob/v1/src/Http/SaloonRequest.php#L110

    $this->setConnector(new $this->connector);
    

    should be easy fix (not tested)

    $this->setConnector(resolve($this->connector::class));
    

    I did a hacky workaround by overriding getConnector() so I can dependency injection my FetchifyConnector to FetchifyUKPostcodeLookupRequest https://github.com/Sammyjo20/Saloon/blob/v1/src/Http/SaloonRequest.php#L120

    <?php
    
    declare(strict_types=1);
    
    namespace App\Http\Saloon\Requests\Fetchify;
    
    use App\Http\Saloon\Connectors\Fetchify\FetchifyConnector;
    use App\Http\Saloon\Responses\Fetchify\FetchifyUKPostcodeLookupResponse;
    use App\Libraries\Helpers\LaravelGlobals;
    use Sammyjo20\Saloon\Constants\Saloon;
    use Sammyjo20\Saloon\Http\SaloonRequest;
    use Sammyjo20\Saloon\Http\SaloonConnector;
    
    class FetchifyUKPostcodeLookupRequest extends SaloonRequest
    {
        /**
         * Define the method that the request will use.
         *
         * @var string|null
         */
        protected ?string $method = Saloon::GET;
    
        /**
         * Define a custom response that the request will return.
         *
         * @var string|null
         */
        protected ?string $response = FetchifyUKPostcodeLookupResponse::class;
        private string $postcode;
    
        public function __construct(
            private readonly FetchifyConnector              $fetchifyConnector,
            private readonly LaravelGlobals                 $laravelGlobals,
        ) {
    
        }
    
        /**
         * Need to set like this to get it working with dependency injection
         *
         * @return SaloonConnector
         */
        public function getConnector(): SaloonConnector
        {
            return $this->fetchifyConnector;
        }
    
        /**
         * @link https://fetchify.com/docs/json-api/uk-postcode-lookup.html#full-address-rapidaddress
         * @return array
         */
        public function defaultQuery(): array
        {
            return [
                'key' => $this->laravelGlobals->config('services.fetchify.key'),
                'include_geocode' => false,
            ];
        }
    }
    
    
    <?php
    
    declare(strict_types=1);
    
    namespace App\Http\Saloon\Connectors\Fetchify;
    
    use App\Libraries\Helpers\LaravelGlobals;
    use Sammyjo20\Saloon\Http\SaloonConnector;
    use Sammyjo20\Saloon\Traits\Plugins\AcceptsJson;
    
    class FetchifyConnector extends SaloonConnector
    {
        use AcceptsJson;
    
        public function __construct(private LaravelGlobals $laravelGlobals)
        {
        }
    
        /**
         * Define the base url of the api.
         * @link https://fetchify.com/docs/json-api/uk-postcode-lookup.html#full-address-rapidaddress
         * @return string
         */
        public function defineBaseUrl(): string
        {
            return $this->laravelGlobals->config('services.fetchify.endpoint');
        }
    }
    

    Hope you got the point, cheers.

    opened by rrolla 5
  • WIP: add response with request

    WIP: add response with request

    Would close #3. However, this is not as trivial as I previously thought. @Sammyjo20 do you know any way how I could get the full namespace and class name of the newly generated response?

    opened by Wulfheart 4
  • `\Sammyjo20\Saloon\Http\SaloonRequest::getFullRequestUrl()` does not contain query params

    `\Sammyjo20\Saloon\Http\SaloonRequest::getFullRequestUrl()` does not contain query params

    I assumed that the \Sammyjo20\Saloon\Http\SaloonRequest::getFullRequestUrl method returns, well, the full request URL. Fr me this includes the query params as well.

    How can I retrieve the full URL used for the request from the response? πŸ€” Using $request->getQuery() doesn't 100% feel right/reliable as theoretically the return value could change between running the request and asking for the used query string.

    opened by Gummibeer 3
  • Commands for Responses

    Commands for Responses

    Related to https://github.com/Sammyjo20/Saloon/issues/11.

    I think we should add a command php artisan saloon:response and also a flag to pa saloon:request SomeRequest -r SomeResponse which already prefills the request.

    What do you think about it?

    opened by Wulfheart 3
  • Folder Structure

    Folder Structure

    Currently the cli commands generate requests, responses and connectors in separate folders. However, when writing multiple API integrations it becomes messy when these three from different applications become mixed up. Therefore I propose an refined command line experience consisting of the following:

    1. I like the location in the Http folder as it deals with outgoing Http requests. I don't like the name Saloon for that folder. I suppose it is for branding purposes but I don't think that it is the right way forward because it doesn't clearly convey the purpose of the directory. I think it should be named something like Integration.
    2. Furthermore I fear that different integrations become mixed up in a context with multiple integrations. Therefore I think it should contain multiple directories with the current structure in them (but the Connectors directory omitted as there is only one connector per API I suppose).
    app/
    β”œβ”€ Http/
    β”‚  β”œβ”€ Integration/
    β”‚  β”‚  β”œβ”€ Integration1/
    β”‚  β”‚  β”‚  β”œβ”€ Requests/
    β”‚  β”‚  β”‚  β”œβ”€ Responses/
    β”‚  β”‚  β”‚  β”œβ”€ Integration1Connector.php
    β”‚  β”‚  β”œβ”€ Integration2/
    β”‚  β”‚  β”‚  β”œβ”€ Requests/
    β”‚  β”‚  β”‚  β”œβ”€ Responses/
    β”‚  β”‚  β”‚  β”œβ”€ Integration2Connector.php
    
    1. Number 2 would also lead to an additional flag called something like -i|--integration to specify which integration is intended to have an addition.

    What do you think about it?

    I'd be willing to contribute this addition.

    opened by Wulfheart 2
  • BaseUrl gets an '/' at the beginning if empty

    BaseUrl gets an '/' at the beginning if empty

    Hello guys,

    thank you for this wonderful package.

    I am developing a connector for Google OAuth.

    Google provides three completely different Base URL's for Authorization, Token and User.

    My plan was to define each complete URL as an Endpoint in the OAuthConfig and leave the BaseUrl empty.

    Doing so results in a request URL that always starts with an '/' in the beginning which makes the curl fail.

    For example:

    $connector->getAuthorizationUrl() // => /https://accounts.google.com/o/...

    Same is true for The Token & User Endpoint.

    Now I know I could create 3 different connectors for each endpoint and provide a non empty BaseURL and the Endpoint but then I would have 3 files instead of one and it kinda fails the purpose of this flow in my opinion.

    I think it's an easy fix to skip the beginning '/' if the BaseUrl is empty.

    class YoutubeOAuthConnector extends SaloonConnector
    {
        use AuthorizationCodeGrant;
    
        public function defineBaseUrl(): string
        {
            return '';
        }
    
        protected function defaultOauthConfig(): OAuthConfig
        {
            return OAuthConfig::make()
                ->setClientId(config('youtube.api.client_id'))
                ->setClientSecret(config('youtube.api.client_secret'))
                ->setDefaultScopes(config('youtube.api.scopes'))
                ->setAuthorizeEndpoint('https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent')
                ->setTokenEndpoint('https://oauth2.googleapis.com/token')
                ->setUserEndpoint('https://www.googleapis.com/oauth2/v3/userinfo')
                ->setRedirectUri(route(config('youtube.api.redirect_uri')));
        }
    }
    
    opened by Lawebso 1
  • Group requests on connector

    Group requests on connector

    It would be cool to group requests by any meaning - for example the primary entity. That way the method names would be shorter. A real problem I see with that is the missing autocompletion. Right now you can at least add @method AllMoviesRequest allMoviesRequest() doc-block. That's not possible anymore as I only can annotate the ->movies property but not the methods available on it.

    $requests = [
        'movies' => [
            'all' => AllMoviesRequest::class,
        ],
    ];
    
    $sdk->movies->all()->send()->json();
    

    I assume that it would be added to the core with a new class Proxy (tbd) which retrieves an instance of the connector and the array of method to requests mappings. Benefit: this could be easily enhanced to a multi-level thing like GitHub API for example $github->user->repo->show($username, $reponame) As it would just pen another instance of that proxy.

    opened by Gummibeer 1
  • Record real responses without mocking requests

    Record real responses without mocking requests

    With Laravel HTTP client you can record responses from real requests. This is useful to generate an initial fixture set for your tests and don't have to create them manually. In the best case Saloon would work the same - so it would switch to a partial mocking approach and record every mocked and real response. Possibly with an isMocked = true flag but not important. That way you could list unmocked requests somewhere and automatically create fixtures to make them mocked in the next run or whatever you want but don't break/fail the execution.

    In Laravel it's part of the partial HTTP client mocking approach. As soon as you call Http::fake() it will record every request/response.

    • \Illuminate\Http\Client\Factory::record
    • \Illuminate\Http\Client\Factory::recorded

    Below an example from TMDB test code:

    protected function tearDown(): void
    {
        foreach (Http::recorded() as $record) {
            /**
             * @var \Illuminate\Http\Client\Request $request
             * @var \Illuminate\Http\Client\Response $response
             */
            [$request, $response] = $record;
    
            if ($response->successful()) {
                $filepath = $this->fixturePath(
                    Str::after(parse_url($request->url(), PHP_URL_PATH), '/3/'),
                    parse_url($request->url(), PHP_URL_QUERY)
                );
    
                File::ensureDirectoryExists(dirname($filepath));
                File::put(
                    $filepath,
                    json_encode($response->json(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
                );
            }
        }
    
        parent::tearDown();
    }
    
    opened by Gummibeer 1
  • Mock Response by Closure

    Mock Response by Closure

    With Laravel HTTP client you can mock responses from a Closure. This is super useful for larger fixture sets following a simple URL to path transformation. Would be great if that would be possible with Saloon too - as it's not a real option to create thousands of mock responses and URL patterns from a list of fixture files.

    Below an example from TMDB test code:

    Http::fake([
        'https://api.themoviedb.org/3/*' => function (Request $request): PromiseInterface {
            try {
                $body = $this->fixture(
                    Str::after(parse_url($request->url(), PHP_URL_PATH), '/3/'),
                    parse_url($request->url(), PHP_URL_QUERY)
                );
            } catch (FileNotFoundException $e) {
                return Http::response([
                    'error' => [
                        'message' => $e->getMessage(),
                        'trace' => $e->getTraceAsString(),
                    ],
                ], Response::HTTP_NOT_FOUND);
            }
    
            return Http::response($body, Response::HTTP_OK);
        },
    ]);
    
    opened by Gummibeer 1
  • Fix | V2 - Fixed Tests

    Fix | V2 - Fixed Tests

    • Fixing tests
    • Fixing stubs
    • Fixed stub
    • Working with the latest v2.x branch
    • Updated stubs
    • Cleaned up the HttpSender
    • Swap back to GuzzleSender for stability
    • Fixed other tests
    opened by Sammyjo20 0
  • TestResponse instance for test responses

    TestResponse instance for test responses

    In Laravel you have access to TestResponse if you call one of your own endpoints in a test case. This response gives you access to a ton of assertions - headers, status, JSON and with packages even HTML...

    How about reusing this class or creating a custom one for requests in tests? πŸ€”

    $connector->user()->get($id)
      ->assertStatus(200)
      ->assertHeader('x-header', 'foobar')
      ->assertJson(fun(AssertableJson) => ...);
    
    opened by Gummibeer 0
  • Send requests / responses to laravel telescope?

    Send requests / responses to laravel telescope?

    Hi, first thanks for the nice package, but as always it's still room to improve it.

    Laravel telescope has a really nice feature HTTP Client watcher records outgoing HTTP client requests made by your application.

    telescope

    Is it plan to send requests / responses to laravel telescope?

    opened by rrolla 9
Releases(v1.6.0)
  • v1.6.0(Dec 11, 2022)

    What's Changed

    • Fix | Support PHP 8.2 by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/33

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.5.3...v1.6.0

    Source code(tar.gz)
    Source code(zip)
  • v1.5.3(Oct 25, 2022)

    What's Changed

    • Fix | Saloon facade alias by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/28

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.5.2...v1.5.3

    Source code(tar.gz)
    Source code(zip)
  • v1.5.2(Oct 24, 2022)

  • v1.5.1(Sep 25, 2022)

    What's Changed

    • Fix | Fixed matrix tests for older versions of Laravel by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/25

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.5.0...v1.5.1

    Source code(tar.gz)
    Source code(zip)
  • v1.5.0(Jul 22, 2022)

    What's Changed

    • Feature | Added Laravel Events by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/23

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.4.0...v1.5.0

    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(Jun 13, 2022)

    • Introduced support for Saloon v1.4.0 which introduces closure-mocking.

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.3.1...v1.4.0

    Source code(tar.gz)
    Source code(zip)
  • v1.3.1(Jun 12, 2022)

  • v1.3(Jun 10, 2022)

    What's Changed

    • Feature | Added a new OAuth2 encrypted cast by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/20

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.2...v1.3

    Source code(tar.gz)
    Source code(zip)
  • v1.2(Jun 9, 2022)

    Support for Saloon v1.3 which introduces support for OAuth2's Authorization Code Flow.

    Click here to read more: https://docs.saloon.dev/advanced/oauth2-authentication

    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(May 23, 2022)

    What's Changed

    • Fixed facade doc-block by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/18

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v1.1.0...v1.1.1

    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(May 22, 2022)

    This release introduces Recorded responses. You can now ask Saloon to record your responses. This is really useful if you want to record real responses for fixtures.

    Methods

    use Sammyjo20\SaloonLaravel\Facades\Saloon;
    
    Saloon::record();
    Saloon::isRecording();
    Saloon::getRecordedResponses();
    Saloon::getLastRecordedResponse();
    Saloon::stopRecording();
    
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Apr 13, 2022)

  • v0.12.0(Apr 13, 2022)

    What's Changed

    • Mock Exceptions by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/12

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.11.0...v0.12.0

    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Apr 11, 2022)

    What's Changed

    • Feature | Authenticators by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/11

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.10.0...v0.11.0

    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Mar 3, 2022)

    What's Changed

    • Upgraded to Saloon v0.9 by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/10

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.9.1...v0.10.0

    Source code(tar.gz)
    Source code(zip)
  • v0.9.1(Feb 28, 2022)

  • v0.9.0(Feb 28, 2022)

    What's Changed

    • Upgraded to Saloon v0.8 by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/9

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.8.0...v0.9.0

    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(Feb 7, 2022)

    What's Changed

    • Added mocking assertions by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/8

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.7.0...v0.8.0

    Source code(tar.gz)
    Source code(zip)
  • v0.7.0(Feb 5, 2022)

    Breaking Changes

    • Changed the folder where the commands creates Saloon files. Thank you @Wulfheart for the PR!

    What's Changed

    • Namespace Fix by @Wulfheart in https://github.com/Sammyjo20/saloon-laravel/pull/5
    • Upgraded to Saloon 0.6.6 by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/7

    New Contributors

    • @Wulfheart made their first contribution in https://github.com/Sammyjo20/saloon-laravel/pull/5

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.6.0...v0.6.6

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Jan 28, 2022)

  • v0.5.0(Jan 28, 2022)

    What's Changed

    • Updated Saloon version to v0.5.0
    • Added new commands by @Sammyjo20 in https://github.com/Sammyjo20/saloon-laravel/pull/3

    Full Changelog: https://github.com/Sammyjo20/saloon-laravel/compare/v0.4.1...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Jan 23, 2022)

  • v0.3.1(Jan 20, 2022)

  • v0.3.0(Jan 17, 2022)

  • v0.2.0(Jan 15, 2022)

  • v0.0.1(Jan 14, 2022)

Owner
Sam CarrΓ©
Financial services web developer. Laravel, Livewire, Vue, TALL. 🀠
Sam CarrΓ©
A Laravel chat package. You can use this package to create a chat/messaging Laravel application.

Chat Create a Chat application for your multiple Models Table of Contents Click to expand Introduction Installation Usage Adding the ability to partic

Tinashe Musonza 931 Dec 24, 2022
This package provides extended support for our spatie/enum package in Laravel.

Laravel support for spatie/enum This package provides extended support for our spatie/enum package in Laravel. Installation You can install the packag

Spatie 264 Dec 23, 2022
Testbench Component is the de-facto package that has been designed to help you write tests for your Laravel package

Laravel Testing Helper for Packages Development Testbench Component is the de-facto package that has been designed to help you write tests for your La

Orchestra Platform 1.9k Dec 29, 2022
πŸ₯³πŸ” This package is a Laravel package that checks if an email address is a spammer

This package is a Laravel package that checks if an email address is a spammer. It verifies your signups and form submissions to confirm that they are legitimate.

Endurance, the Martian 15 Dec 19, 2022
GeoLocation-Package - This package helps you to know the current language of the user, the country from which he is browsing, the currency of his country, and also whether he is using it vpn

GeoLocation in PHP (API) ?? ?? ?? This package helps you to know a lot of information about the current user by his ip address ?? ?? ?? This package h

Abdullah Karam 4 Dec 8, 2022
Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

null 9 Dec 14, 2022
List of 77 languages for Laravel Framework 4, 5, 6, 7 and 8, Laravel Jetstream , Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova and Laravel Spark.

Laravel Lang In this repository, you can find the lang files for the Laravel Framework 4/5/6/7/8, Laravel Jetstream , Laravel Fortify, Laravel Cashier

Laravel Lang 6.9k Jan 2, 2023
A package that uses blade templates to control how markdown is converted to HTML inside Laravel, as well as providing support for markdown files to Laravel views.

Install Install via composer. $ composer require olliecodes/laravel-etched-blade Once installed you'll want to publish the config. $ php artisan vendo

Ollie Codes 19 Jul 5, 2021
A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic

A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic! concepts like , Hijri Dates & Arabic strings and so on ..

Adnane Kadri 49 Jun 22, 2022
A Laravel package that adds a simple image functionality to any Laravel model

Laraimage A Laravel package that adds a simple image functionality to any Laravel model Introduction Laraimage served four use cases when using images

Hussein Feras 52 Jul 17, 2022
laravel-vat is a package that contains the Laravel related wiring code for ibericode/vat

laravel-vat is a package that contains the Laravel related wiring code for ibericode/vat, helping you deal with VAT legislation for businesses based in the EU.

Danny van Kooten 117 Dec 5, 2022
Laravel Backup Panel provides a dashboard for spatie/laravel-backup package.

Laravel Backup Panel Laravel Backup Panel provides a dashboard for spatie/laravel-backup package. It lets you: create a backup (full | only database |

Pavel Mironchik 366 Dec 6, 2022
Laravel package to find performance bottlenecks in your laravel application.

Laravel Meter Laravel Meter monitors application performance for different things such as requests, commands, queries, events, etc and presents result

Sarfraz Ahmed 230 Dec 27, 2022
Laravel 2-Step Verification is a package to add 2-Step user authentication to any Laravel project easily.

Laravel 2-Step verification is a package to add 2-Step user authentication to any Laravel project easily. It is configurable and customizable. It uses notifications to send the user an email with a 4-digit verification code. Laravel 2-Step Authentication Verification for Laravel. Can be used in out the box with Laravel's authentication scaffolding or integrated into other projects.

Jeremy Kenedy 204 Dec 23, 2022
This package provides an integration with FFmpeg for Laravel. Laravel's Filesystem handles the storage of the files.

Laravel FFMpeg This package provides an integration with FFmpeg for Laravel 6.0 and higher. Laravel's Filesystem handles the storage of the files. Lau

Protone Media 1.3k Jan 1, 2023
Laravel Users | A Laravel Users CRUD Management Package

A Users Management Package that includes all necessary routes, views, models, and controllers for a user management dashboard and associated pages for managing Laravels built in user scaffolding. Built for Laravel 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 6.0, 7.0 and 8.0.

Jeremy Kenedy 393 Nov 28, 2022
Laravel-FCM is an easy to use package working with both Laravel and Lumen for sending push notification with Firebase Cloud Messaging (FCM).

Laravel-FCM Introduction Laravel-FCM is an easy to use package working with both Laravel and Lumen for sending push notification with Firebase Cloud M

Rahul Thapa 2 Oct 16, 2022
Easily add a full Laravel blog (with built in admin panel and public views) to your laravel project with this simple package.

Webdevetc BlogEtc - Complete Laravel Blog Package Quickly add a blog with admin panel to your existing Laravel project. It has everything included (ro

WebDevEtc. 227 Dec 25, 2022
Laravel-Mediable is a package for easily uploading and attaching media files to models with Laravel 5.

Laravel-Mediable Laravel-Mediable is a package for easily uploading and attaching media files to models with Laravel. Features Filesystem-driven appro

Plank Design 654 Dec 30, 2022