Identify app models with a URI. Inspired by the globalid gem.

Overview

GlobalIds Laravel

Total Downloads License

Identify app models with a URI. Heavily inspired by the globalid gem.

Global ID - Reference models by URI

A Global ID is an app wide URI that uniquely identifies a model instance:

gid://YourApp/Some\\Model/id

This is helpful when you need a single identifier to reference different classes of objects.

One example is storing model references in places where you cannot enforce constraints or cannot make use of the convenient Eloquent relationships, such as storing model references in a rich text content field. We need to reference a model object rather than serialize the object itself. We can pass a Global ID that can be used to locate the model when it's time to render the rich text content. The rendering doesn't need to know the details of model naming and IDs, just that it has a global identifier that references a model.

Another example is a drop-down list of options, consisting of both Users and Groups. Normally we'd need to come up with our own ad hoc scheme to reference them. With Global IDs, we have a universal identifier that works for objects of both classes.

Installation

Via Composer:

composer require tonysm/globalid-laravel

Usage

Add the HasGlobalIdentification trait into any model with a find($id), findMany($ids): Collection static methods, and a getKey() instance method.

$personGid = Person::find(1)->toGlobalId();
# => Tonysm\GlobalId\GlobalId {#5010}

$personGid->toString();
# => "gid://laravel/App%5CModels%5CPerson/1"

# Returns a URL-safe base64 encoded version of the SGID...
$personGid->toParam();
# => "Z2lkOi8vbGFyYXZlbC9BcHAlNUNNb2RlbHMlNUNQZXJzb24vMQ"

Tonysm\GlobalId\Facades\Locator::locate('gid://laravel/App%5CModels%5CPerson/1');
# => App\Models\Person {#5022 id:1...

# You can also pass the base64 encoded to it and it will just work...
Tonysm\GlobalId\Facades\Locator::locate('Z2lkOi8vbGFyYXZlbC9BcHAlNUNNb2RlbHMlNUNQZXJzb24vMQ');
# => App\Models\Person {#5022 id:1...

# You can also call the locate method on the GlobalId object...
$personGid->locate();
# => App\Models\Person {#5022 id:1...

If you don't want to implement the finders methods in the model class (Eloquent already has them), see custom locators below.

Signed Global IDs

For added security GlobalIDs can also be signed to ensure that the data hasn't been tampered with.

$personSgid = Person::find(1)->toSignedGlobalId();
# => Tonysm\GlobalId\SignedGlobalId {#5005}

$personSgid = Person::find(1)->toSgid();
# => Tonysm\GlobalId\SignedGlobalId {#5026}

$personSgid->toString();
# => "BAhJIh5naWQ6Ly9pZGluYWlkaS9Vc2VyLzM5NTk5BjoGRVQ=--81d7358dd5ee2ca33189bb404592df5e8d11420e"

Tonysm\GlobalId\Facades\Locator::locateSigned($personSgid);
# => App\Models\Person {#5009 id: 1, ...

# You can also call the locate method on the SignedGlobalId object...
$personSgid->locate();
# => App\Models\Person {#5022 id:1...

Expiration

Signed Global IDs can expire some time in the future. This is useful if there's a resource people shouldn't have indefinite access to, like a share link.

$expiringSgid = Document::find(5)->toSgid([
    'expires_at' => now()->addHours(2),
    'for' => 'sharing',
]);
# => Tonysm\GlobalId\SignedGlobalId {#5026}

# Within 2 hours...
Tonysm\GlobalId\Facades\Locator::locateSigned($expiringSgid->toString(), [
    'for' => 'sharing',
]);
# => App\Models\Document {#5009 id: 5, ...

# More than 2 hours later...
Tonysm\GlobalId\Facades\Locator::locateSigned($expiringSgid->toString(), [
    'for' => 'sharing',
]);
# => null

An auto-expiry of 1 month is set by default. You can override this default by passing a expiration resolver Closure from any Service Provider boot method. This resolver will get called every time a SGID is created:

SignedGlobalId::useExpirationResolver(() => now()->addMonths(3));

This way any generated SGID will use that relative expiry.

It's worth noting that expiring SGIDs are not idempotent because they encode the current timestamp; repeated calls to to_sgid will produce different results. For example:

Document::find(5)->toSgid()->toString() == Document::find(5)->toSgid()->toString()
# => false

You need to explicitly pass ['expires_at' => null] to generate a permanent SGID that will not expire,

# Passing a false value to either expiry option turns off expiration entirely.
$neverExpiringSgid = Document::find(5)->toSgid(['expires_at' => null]);
# => Tonysm\GlobalId\SignedGlobalId {#5026}

# Any time later...
Tonysm\GlobalId\Facades\Locator::locateSigned($neverExpiringSgid);
# => App\Models\Document {#5009 id: 5, ...

Purpose

You can even bump the security up some more by explaining what purpose a Signed Global ID is for. In this way evildoers can't reuse a sign-up form's SGID on the login page. For example:

$signupPersonSgid = Person::find(1)->toSgid(['for' => 'signup_form']);
# => Tonysm\GlobalId\SignedGlobalId {#5026}

Tonysm\GlobalId\Facades\Locator::locateSigned($signupPersonSgid, ['for' => 'signup_form']);
# => App\Models\Person {#5009 id: 1, ...

Tonysm\GlobalId\Facades\Locator::locateSigned($signupPersonSgid, ['for' => 'login']);
# => null

Locating many Global IDs

When needing to locate many Global IDs use Tonysm\GlobalId\Facades\Locator->locateMany or Tonysm\GlobalId\Facades\Locator::locateManySigned() for Signed Global IDs to allow loading Global IDs more efficiently.

For instance, the default locator passes every $modelId per $modelName thus using $modelName::findMany($ids) versus Tonysm\GlobalId\Facades\Locator->locate()'s $modelName::find($id).

In the case of looking up Global IDs from a database, it's only necessary to query once per $modelName as shown here:

$gids = $users->merge($students)->sortBy('id')->map(fn ($model) => $model->toGlobalId());
# => [#<Tonysm\GlobalId\GlobalId {#5026} @gid=#GID<gid://app/User/1>>,
#<Tonysm\GlobalId\GlobalId {#5027} @gid=#GID<gid://app/Student/1>>,
#<Tonysm\GlobalId\GlobalId {#5028} @gid=#<GID gid://app/User/2>>,
#<Tonysm\GlobalId\GlobalId {#5029} @gid=#<GID gid://app/Student/2>>,
#<Tonysm\GlobalId\GlobalId {#5030} @gid=#<GID gid://app/User/3>>,
#<Tonysm\GlobalId\GlobalId {#5031} @gid=#<GID gid://app/Student/3>>]

Tonysm\GlobalId\Facades\Locator::locateMany($gids);
# SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2, $3)  [["id", 1], ["id", 2], ["id", 3]]
# SELECT "students".* FROM "students" WHERE "students"."id" IN ($1, $2, $3)  [["id", 1], ["id", 2], ["id", 3]]
# => [#<User id: 1>, #<Student id: 1>, #<User id: 2>, #<Student id: 2>, #<User id: 3>, #<Student id: 3>]

Note the order is maintained in the returned results.

Custom App Locator

A custom locator can be set for an app by calling Tonysm\GlobalId\Locator::use() and providing an app locator to use for that app. A custom app locator is useful when different apps collaborate and reference each others' Global IDs. When finding a Global ID's model, the locator to use is based on the app name provided in the Global ID url.

Using a custom Locator:

use Tonysm\GlobalId\GlobalId;
use Tonysm\GlobalId\Facades\Locator;
use Tonysm\GlobalId\Locators\LocatorContract;
use Illuminate\Support\Collection;

Locator::use('foo', new class implements LocatorContract {
    public function locate(GlobalId $globalId)
    {
        // ...
    }

    public function locateMany(Collection $globalIds, array $options = []): Collection
    {
        // ...
    }
});

After defining locators as above, URIs like gid://foo/Person/1 will now use that locator. Other apps will still keep using the default locator.

Testing the Package

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

You're encouraged to submit pull requests, propose features and discuss issues.

Security Vulnerabilities

Drop me an email at [email protected] if you want to report security vulnerabilities.

License

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

Credits

You might also like...
A Dashboard Panel for Shop Admins to control customers using Hyperstream App
A Dashboard Panel for Shop Admins to control customers using Hyperstream App

Overview 🪟 Hyperstream is an application that facilitates simple and easy self-service at supermarkets, eliminating long lines for customers and redu

🔪 WordPress + React Starter Kit: Spin up a WordPress-powered React app in one step
🔪 WordPress + React Starter Kit: Spin up a WordPress-powered React app in one step

Postlight's Headless WordPress + React Starter Kit is an automated toolset that will spin up three things: A WordPress backend that serves its data vi

Laravel app boilerplate with email verification process on registration

The default master branch is for Laravel 5.4 , for Laravel 5.3 boilerplate switch to the v5.3 branch Laravel app boilerplate with email verification o

A CRUD app made with Laravel and VueJS 3 (API Composition)

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Web-app that helps clinicians with interpreting ECG recordings

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

CodeIgniter 4 + Svelte App Starter

CodeIgniter4 + Svelte Application Starter 💡 Features: ⚡ Super fast single page application (SPA). 🔥 Hot Module Replacment (HMR). 🧩 Easy to install

CodeIgniter 4 + Vue3 App Starter

CodeIgniter 4 + Vue3 Application Starter Features: 💡 Super fast single page application (SPA). 🔥 Hot Module Replacment (HMR). 🧩 Easy to install and

Integrates the Trix Editor with Laravel. Inspired by the Action Text gem from Rails.

Integrates the Trix Editor with Laravel. Inspired by the Action Text gem from Rails. Installation You can install the package via composer: composer r

Use auto generated UUID slugs to identify and retrieve your Eloquent models.

Laravel Eloquent UUID slug Summary About Features Requirements Installation Examples Compatibility table Alternatives Tests About By default, when get

URI manipulation Library

URI The Uri package provides simple and intuitive classes to manage URIs in PHP. You will be able to parse, build and resolve URIs create URIs from di

:gem: Simple MySQLi Abstraction Layer + Doctrine/DBAL support

💎 Simple MySQLi Class This is a simple MySQL Abstraction Layer compatible with PHP 7+ that provides a simple and secure interaction with your databas

:gem: Go! AOP PHP - modern aspect-oriented framework for the new level of software development

Go! Aspect-Oriented Framework for PHP Go! AOP is a modern aspect-oriented framework in plain PHP with rich features for the new level of software deve

Quickly identify controller methods with no route in your Laravel applications.
Quickly identify controller methods with no route in your Laravel applications.

Orphan Controller Quickly identify controller methods with no route in your Laravel applications. Installation You can install the package via Compose

From the team that brought you laravel-random-command comes another gem!
From the team that brought you laravel-random-command comes another gem!

💍 Why require one if you can require them all? From the team that brought you laravel-random-command comes another gem! Requiring all our packages se

This is a Laravel port of the local_time gem from Basecamp.

This is a Laravel port of the local_time gem from Basecamp. It makes it easy to display date and time to users in their local time. Its Blade componen

churn-php is a package that helps you identify php files in your project that could be good candidates for refactoring
churn-php is a package that helps you identify php files in your project that could be good candidates for refactoring

churn-php Helps discover good candidates for refactoring. Table of Contents What Is it? Compatibility How to Install? How to Use? How to Configure? Si

Web Shell Detector – is a php script that helps you find and identify php/cgi(perl)/asp/aspx shells.
Web Shell Detector – is a php script that helps you find and identify php/cgi(perl)/asp/aspx shells.

Web Shell Detector – is a php script that helps you find and identify php/cgi(perl)/asp/aspx shells. Web Shell Detector has a “web shells” signature database that helps to identify “web shell” up to 99%. By using the latest javascript and css technologies, web shell detector has a light weight and friendly interface.

A PHP web interface for scanning ISBN book codes, identify books with Antolin reading promotion offer

Ein PHP-Webinterface zum Scannen von ISBN-Buchcodes, identifiziere Bücher mit Antolin-Leseförderungs-Angebot. Einfache Installation. Für Mitarbeiter*innen in Schulbüchereien.

Think-scout - A driver based solution to searching your models. Inspired By Laravel Scout

前言 whereof/think-scout根据thinkphp设计思想参考laravel/scout进行扩展 whereof/think-scout 为模型的全文搜索提供了一个简单的、基于驱动程序的解决方案。 目前,Scout 自带了一个 Elasticsearch 驱动;而编写自定义驱动程序很简

Releases(1.1.0)
Owner
Tony Messias
Tony Messias
An account management Panel based on Laravel7 framework. Include multiple payment, account management, system caching, admin notification, products models, and more.

ProxyPanel 简体中文 Support but not limited to: Shadowsocks,ShadowsocksR,ShadowsocksRR,V2Ray,Trojan,VNET Demo Demo will always on dev/latest code, rather

null 17 Sep 3, 2022
Quickly build an admin interface for your Eloquent models

Website | Documentation | Add-ons | Pricing | Services | Stack Overflow | Reddit | Blog | Newsletter Quickly build an admin interface for your Eloquen

Backpack for Laravel 2.5k Jan 6, 2023
An example of Laravel app that runs in a docker container with RoadRunner

This repository contains an example of Laravel (PHP Framework) application that runs in a docker container with RoadRunner (high-performance PHP appli

Paramtamtam 117 Dec 20, 2022
Simple App Project

SimpleProject(CRUD) by Melvs Clone the repository with git clone Copy .env.example file to .env and edit database credentials there Run composer insta

Melvs 3 Apr 22, 2021
Demo App for Symfony Twig & Live Components

Twig & Live Components Demo Hi there! You've stumbled across a treasure of demos for the the TwigComponent and LiveComponent libraries! Fun! If you wa

Ryan Weaver 48 Jun 27, 2022
todolist app with vueJS frontend and laravel backend

todolist-vuejs-laravel A todolist app with vue.js frontend and laravel backend Demo demo Installation To run this locally, you'll need to clone the re

Isaac Kojo Yeboah 2 May 10, 2022
Symfony React Blank is a blank symfony and react project, use this template to start your app using Symfony as an backend api and React as a frontend library.

Symfony React Blank Symfony React Blank is a blank symfony and react project, use this template to start your app using Symfony as an backend api and

Antoine Kingue 2 Nov 5, 2021
Simple web app with laravel build from scratch

Find Me Simple Web Application This "Find Me" matchmaking web-based application is useful for facilitating people who are looking for new relationship

Jieyab 15 Nov 26, 2022
Boilerplate for a standard tested resourceful Laravel app

Gold Standard A Laravel boilerplate resource This repo is an example of what I consider to be my "gold standard" of a resource-oritented application w

Dwight Watson 25 Mar 19, 2019