PHPackages                             weapnl/laravel-junction - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. weapnl/laravel-junction

ActiveLibrary

weapnl/laravel-junction
=======================

Easily create a REST API with extended functionality, such as eager loading, searching, filtering, and more.

v0.4.6(2mo ago)2211.9k↓46.4%8[4 issues](https://github.com/weapnl/laravel-junction/issues)[6 PRs](https://github.com/weapnl/laravel-junction/pulls)MITPHPPHP ^8.2CI passing

Since Nov 9Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/weapnl/laravel-junction)[ Packagist](https://packagist.org/packages/weapnl/laravel-junction)[ Docs](https://github.com/weapnl/laravel-junction)[ RSS](/packages/weapnl-laravel-junction/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (43)Used By (0)

Laravel-Junction
================

[](#laravel-junction)

This project allows you to easily create a REST API with Laravel. It has extended functionality, such as eager loading, searching, filtering, and more.

Installation
------------

[](#installation)

```
composer require weapnl/laravel-junction
```

### JS/TS Support

[](#jsts-support)

Laravel-Junction has a companion JavaScript/TypeScript package called [JS-Junction](https://github.com/weapnl/js-junction)! This package extends the functionality of our Laravel package to the front end, offering a seamless integration for your web applications.

### Development

[](#development)

In order to easily work on this package locally and use it in another local project, do the following:

1. Add a repository in the `composer.json` file of the project you want to include Laravel-Junction in:

```
"repositories": {
    "laravel-junction": {
        "type": "path",
        "url": "./laravel-junction",
        "options": {
            "symlink": true
        }
    }
}
```

2. If your other project runs in docker, add a volume referencing the folder where Laravel-Junction resides:

```
services:
  api:
    image: ...
    volumes:
      - ../laravel-junction:/var/www/laravel-junction
```

3. Install the local package. The `symlink` option you set on the repository earlier makes sure that you only need to do this once as opposed to every code change.

```
composer require weapnl/laravel-junction dev-main
```

Quick Start
-----------

[](#quick-start)

```
// app/Http/Controllers/Api/UserController.php
namespace App\Http\Controllers\Api;

use Weap\Junction\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * The class name of the model for which the controller should implement CRUD actions.
     *
     * @var string
     */
    public $model = User::class;

    /**
     * Define the relations which can be loaded in a request using "array" notation.
     *
     * @return array
     */
    public function relations(): array
    {
        return [
            'orders',
        ];
    }
```

```
// routes/api.php
Junction::apiResource('users', 'UserController');
```

You're all set and ready to go now. You can now perform requests to the `/api/users` endpoint. Try a post request to create a new user, or a get request to retrieve all users.

Usage
-----

[](#usage)

### Setting up the Controller

[](#setting-up-the-controller)

To make the controller accessible through the api, you need to extend the `Weap\Junction\Http\Controllers\Controller` class. This class extends the default Laravel controller, and adds some extra functionality. Defining the controller is pretty straightforward, check the [Quick start](#quick-start) section for a basic example. We will now go over some of the extra functionality.

```
// app/Http/Controllers/Api/UserController.php
namespace App\Http\Controllers\Api;

use Weap\Junction\Http\Controllers\Controller; // Make sure to import the Controller class from the Weap/Junction package.

class UserController extends Controller
{
    /**
     * The class name of the model for which the controller should implement CRUD actions.
     *
     * @var string
     */
    public $model = User::class;

    /**
     * The class name of Resource to be used for the show and index methods.
     *
     * @var string $resource
     */
    public $resource = UserResource::class;

    /**
     * Define the relations which can be loaded in a request using "array" notation.
     *
     * @return array
     */
    public function relations(): array
    {
        return [
            'orders',
            // Define all your relations here with should be accessible through the API.
        ];
    }
```

#### Sample usage

[](#sample-usage)

```
/api/users?orders[0][column]=id&orders[0][direction]=asc&&search_value=john&search_columns[]=name&search_columns[]=email

```

#### Sample response

[](#sample-response)

The response always contains the properties `items`, `total` and `page`, even if you're not using pagination.

```
{
  "items": [
    {
      "id": 2,
      "name": "John Doe",
      "email": "john.doe@app.com",
      "orders": [],
      "comments": [
        {
          "id": 1,
          "body": "Hello world!"
        }
      ]
    }
  ],
  "total": 1, // Total amount of items
  "page": 1 // The current page
}
```

#### Filters

[](#filters)

Filters are applied to the query. Filters are defined using array keys. Available filters:

KeyExampleDescription`limit``limit=10`Limit the maximum amount of results.`orders``orders[][column]=name,orders[][direction]=asc`Columns to order on.`with``with=[orders,comments]`Relations to load.`scopes``scopes[0][name]=hasName&scopes[0][params][0]=John`Scopes to apply with the given parameters.`search_value``search_value=john`Search for the given value.`search_columns``search_columns[]=id&search_columns[]=name`The columns to search in. (optional: defaults to the searchable variable on your controller.)`wheres``wheres[0][column]=name&wheres[0][operator]=%3D&wheres[0][value]=John (%3D = '=', ASCII Encoding)`Apply where clauses.`where_in``where_in[0][column]=id&where_in[0][values][0]=1&where_in[0][values][1]=2`Apply where in clause. (Where id is 1 or 2)`where_not_in``where_not_in[0][column]=id&where_not_in[0][values][0]=1&where_not_in[0][values][1]=2`Apply where not in clause. (Where id is not 1 or 2)#### Modifiers

[](#modifiers)

Modifiers are applied after the query has run. Available modifiers:

KeyExampleDescription`appends``appends[]=fullname&appends[]=identity.age`Add appends to each model in the result.`hidden_fields``hidden_fields[]=id&hidden_fields[]=address`Hide the given fields for each model in the result.`pluck``pluck[]=id&pluck[]=address.house_number`Only return the given fields for each model in the result. (Only available for `index` and `show` routes)#### Pagination

[](#pagination)

Pagination is applied on database-level (after applying all filters). The following parameters can be used to setup pagination:

KeyExampleDescription`paginate``paginate=25`Paginate the result. This also specifies the amount of items per page.`page``page=1`The page to get. Defaults to 1. Requires `paginate` to be set.`page_for_id``page_for_id=1`This will search the correct page based on the given model id. `page` is used as a fallback if the given id can not be found. Requires `paginate` to be set.#### Simple pagination

[](#simple-pagination)

Simple pagination almost the same as the pagination above. But the simple pagination doesn't return the total amount of items or a page number. This is useful for large database tables where the normal pagination is too slow.

KeyExampleDescription`paginate``paginate=25`This specifies the amount of items per page.`simple_pagination``simple_pagination=true`This defines that simple pagination should be used.### Relations

[](#relations)

To limit the relations which can be loaded using the `with` filter, you can override the `relations` method on your controller. This method should return an array containing relations (dot-notation is supported). To add filters to the relation query, you can use the key as relation name and a closure as the value.

**Note**: When using dot-notation, if a closure is given for one of the higher-level relations in your controller, that closure will be applied to the query. For example with relations implemented like below, loading the relation `user.activities`, will apply the `isAdmin` scope to the user query.

```
public function relations()
{
    return [
        'user' => fn ($query) => $query->isAdmin(),
        'user.activities',
    ];
}
```

#### Extensions

[](#extensions)

To modify the relations array before it is parsed, you can use the following in a service provider;

```
app(\Weap\Junction\Extensions\RelationExtension::class)->add(function (array $relations, \Illuminate\Routing\Controller $controller) {
    // Only get activities of the past week
    $relations['activities'] = fn ($query) => $query->where('created_at', '>', now()->subWeek());

    return $relations;
});
```

This example will add or replace the `activities` relation on any controller. Note that it only sets the relation on the controller; it does not actually load the relation unless it is requested.

### Accessors

[](#accessors)

To append accessors to models in the response, you can use the `appends` modifier. This modifier allows dot-notation for relations. These relations will be eager loaded (relation closures defined in the controller are applied as well).

In some cases you may want an accessor to return a value of one of its relations, which would normally cause a lazy load when the accessor is executed. To eager load the relation, you could add it to the request using the `with` filter, which means the frontend is responsible for proper eager loading. To shift this responsibility to the backend, you can use the `Junction::makeAttribute()` function to make an accessor instead of `Attribute::make()`:

```
use Weap\Junction\Junction;

/**
 * @return Attribute
 */
protected function name(): Attribute
{
    return Junction::makeAttribute(
        get: fn (): string => $this->contact->name,
        with: ['contact'],
    );
}
```

In this case, the `contact` relation would be eager loaded before the accessor is executed, which can prevent N+1 queries. The `with` array also allows dot-notation and closures to customize the query:

```
use Weap\Junction\Junction;

/**
 * @return Attribute
 */
protected function name(): Attribute
{
    return Junction::makeAttribute(
        get: function (): string {
            return match ($this->subjectable::class) {
                Employee::class => $this->subjectable->contact?->name,
                Freelancer::class => $this->subjectable->company?->name,
                default => null,
            } ?: '';
        },
        with: [
            'subjectable' => function ($query) {
                $query->morphWith([
                    Employee::class => ['contact'],
                    Freelancer::class => ['company'],
                ]);
            },
        ],
    );
}
```

### Search

[](#search)

This package supports search functionality for given models and relations. On your controller, add a searchable property like defined below. When you want to search a model, add "search\_value" to your request. Optionally you can add "search\_columns" to override the columns from your controller.

```
public $searchable = [
    'id',
    'name',
    'orders.order_number',
];
```

### Resources

[](#resources)

To use resources, set the `resource` variable in your controller. Your resource must extend `\Weap\Junction\Http\Controllers\Resources`.

This allows you to specify which attributes, accessors and relations will be returned. To do this, override the corresponding method:

- `availableAttributes`. Return an array of strings, specifying which attributes will be returned. The primary key is always included.
- `availableAccessors`. Return an array of strings, specifying which accessors will be returned.
- `availableRelations`. Return an array of key/value pairs, where the key is the name of the relation, and the value is another resource.

Return `null` in any of these methods to allow `ALL` attributes/accessors/relations to be returned.

Example:

```
class UserResource extends BaseResource
{
    /**
     * @return array|null
     */
    protected function availableAttributes(): ?array
    {
        return [
            'first_name'
        ];
    }

    /**
     * @return array|null
     */
    protected function availableAccessors(): ?array
    {
        return [
            'fullName'
        ];
    }

    /**
     * @return array|null
     */
    protected function availableRelations(): ?array
    {
        return [
            'orders' => OrderResource::class,
        ];
    }
}
```

### Actions

[](#actions)

This package also supports action routes.

Add the action method in your controller:

```
/**
 * @param null|Model $model
 */
protected function actionSomeName($model = null)
{
    //
}
```

- If you are using policies, your policy should implement the `action` policy, which receives the model as parameter.
- Now, you can call the following route as a `PUT` request: `/api/users`. In the body, add the following (the id is optional):

```
{
    "action": "someName",
    "id": 1
}
```

- You can add as many actions as you want. Just make sure to prefix the method with `action`.

### Index route options

[](#index-route-options)

#### Enforce order by model key

[](#enforce-order-by-model-key)

To enable the option to enforce an order by on the query, set the `junction.route.index.enforce_order_by_model_key` config value to `true`. This will add an "order by" clause to the query based on the model's key name, if no "order by" is already present for the key name. The default order direction is `asc`, but if you want it to use `desc`, update the `junction.route.index.enforce_order_by_model_key_direction` config value to `desc`.

### Validation

[](#validation)

#### FormRequest validation

[](#formrequest-validation)

To validate the incoming request, you can create a `FormRequest` and extend the `Weap\Junction\Http\Controllers\Requests\DefaultFormRequest` class. This class extends the default Laravel `FormRequest` class, and adds some extra functionality.

#### Standard validation

[](#standard-validation)

To validate the request, create a request file for your model and add this to the controller.

```
/**
 * The class name of FormRequest to be used for the store and update methods.
 *
 * @var string
 */
public $formRequest = ModelRequest::class;
```

- Add rules to the `rules()` method in the `ModelRequest`.

```
/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'first_name' => 'required',
    ];
}

/**
 * Define validation rule messages for store and update requests.
 *
 * @return array
 */
public function messages()
{
    return [
        'first_name.required' => 'The first name is required.',
    ];
}
```

#### Save fillable attributes

[](#save-fillable-attributes)

By default, only validated attributes are saved when calling store/update routes. To save fillable attributes instead, set the following on your controller:

```
/**
 * Set to true to save fillable instead of validated attributes in store/update methods.
 *
 * @var bool
 */
protected $saveFillable = true;
```

### Using Temporary Media Files with [Spatie Medialibrary](https://spatie.be/docs/laravel-medialibrary/v11/introduction)

[](#using-temporary-media-files-with-spatie-medialibrary)

#### Step 1: Uploading Files via the API

[](#step-1-uploading-files-via-the-api)

To upload files through the API, use the `/media/upload` endpoint. Include an array of files under the `files` key in the request body. These files will be temporarily stored in the media library and linked to a `MediaTemporaryUpload` model.

**Example Upload Request:**

```
{
    "files": [, ...]
}
```

#### Step 2: Attaching Files to a Model

[](#step-2-attaching-files-to-a-model)

##### Example A: Updating an Existing Model

[](#example-a-updating-an-existing-model)

To attach files to an existing model, include the media IDs in a `PUT` request. For instance, if you want to attach an identity document to an employee, your `PUT` request to `/employees/3` might look like this, assuming the identity files are stored in the `IdentityFiles` [collection](https://spatie.be/docs/laravel-medialibrary/v11/working-with-media-collections/simple-media-collections) on the `Employee` model:

```
{
    "first_name": "John",
    "last_name": "Doe",
    "_media": {
        "IdentityFiles": [1]
    }
}
```

The API will search the request body for the `_media` key and associate the specified media IDs with the correct collection. In this example, media ID 1 will be attached to the `IdentityFiles` collection of the `Employee` with ID 3.

##### Example B: Creating a New Model

[](#example-b-creating-a-new-model)

You can also attach files when creating a new model using a `POST` request. For example, if you are creating a new `Employee` and want to attach a profile picture, your `POST` request to `/employees` might look like this:

```
{
    "first_name": "Jane",
    "last_name": "Smith",
    "_media": {
        "ProfilePicture": [2]
    }
}
```

This will attach media ID 2 to the `ProfilePicture` collection of the newly created `Employee`.

#### Using the `_media` Key in Nested Structures

[](#using-the-_media-key-in-nested-structures)

The `_media` key can also be nested within the request body, for example, inside a `contact` key. In this case, the API will attach the media files to the `contact` relationship of the `Employee`, whether you are creating a new model or updating an existing one.

**Example Request with Nested `_media` Key (for creation):**

```
{
    "first_name": "Jane",
    "last_name": "Smith",
    "contact": {
        "phone": "123-456-7890",
        "_media": {
            "ProfilePicture": [3]
        }
    }
}
```

In this example, when creating a new `Employee`, media ID 3 will be attached to the `ProfilePicture` collection within the `contact` relationship of the new `Employee`.

###  Health Score

52

—

FairBetter than 96% of packages

Maintenance86

Actively maintained with recent releases

Popularity36

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~27 days

Recently: every ~61 days

Total

32

Last Release

62d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3b9003629d6692c37b8584e59af6f556d9b57d413bcbc9ade917f85b131b1414?d=identicon)[RobinvdA](/maintainers/RobinvdA)

![](https://www.gravatar.com/avatar/1077e498c2d4272cfda5affae580da8ce4cbf0a3acbfb6380e053580d69a0cff?d=identicon)[JurianVW](/maintainers/JurianVW)

---

Top Contributors

[![jobjen02](https://avatars.githubusercontent.com/u/25797291?v=4)](https://github.com/jobjen02 "jobjen02 (89 commits)")[![robinvda](https://avatars.githubusercontent.com/u/9365082?v=4)](https://github.com/robinvda "robinvda (51 commits)")[![JurianVW](https://avatars.githubusercontent.com/u/14749269?v=4)](https://github.com/JurianVW "JurianVW (22 commits)")[![RobinWEAP](https://avatars.githubusercontent.com/u/43961532?v=4)](https://github.com/RobinWEAP "RobinWEAP (21 commits)")[![LarsWeap99](https://avatars.githubusercontent.com/u/160250595?v=4)](https://github.com/LarsWeap99 "LarsWeap99 (2 commits)")

###  Code Quality

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/weapnl-laravel-junction/health.svg)

```
[![Health](https://phpackages.com/badges/weapnl-laravel-junction/health.svg)](https://phpackages.com/packages/weapnl-laravel-junction)
```

###  Alternatives

[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11120.2M21](/packages/anourvalar-eloquent-serialize)[namu/wirechat

A Laravel Livewire messaging app for teams with private chats and group conversations.

54324.5k](/packages/namu-wirechat)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135192.6k5](/packages/statamic-rad-pack-runway)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
