Download the PHP package tranzakt/laravel-softdeletesunique without Composer

On this page you can find all versions of the php package tranzakt/laravel-softdeletesunique. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-softdeletesunique

Laravel-SoftDeletesUnique

The purpose of this extension is to make SQL unique constraints (Unique Indexes) work when you are using softDeletes on a table.

Suppose you have a model with id and name fields - and you want name to be unique, so you create a Unique Index on the "name" field. If you create a record for 'Pete' and then (hard) delete it, then you can (of course) create a new record for 'Pete' without the name being duplicated.

However, you then decide to use Soft Deletes, which adds a deleted_at field which is null for non-deleted records, and has a timestamp if the record is deleted.

The idea, of course, is that models with Soft Delete work the same as it would without it, except that you can restore the deleted records. But if you create a record for 'Pete' and then soft delete it, then you want the model to behave the same as if you had hard deleted it and still allow you to create a new (non-deleted) record for 'Pete' alongside the deleted version. You therefore want the combination of deleted_at and name to be unique, so you create a unique index on ['deleted_at', 'name'] expecting it to prevent duplicates, i.e. in your migration, you replace...

with:

However there is a gotcha here that is just waiting to getcha! (and you probably won't explicitly test for this and it will be a problem waiting to happen).

Unfortunately most (but not all) SQL RDBMS follow the SQL standard which defines every NULL value as being different from every other NULL value. Yes NULL != NULL (and that is NOT a typo!!), and that means that the unique index bizarrely allows you to have multiple entries [NULL, 'Pete']!!!, meaning that the Unique Index does not prevent duplicate records from being added.

This is the problem that this package solves.

It does it by creating a new column deleted_at_uniqueable which is maintained as a string version of the deleted_at column; using the empty string '' if the deleted_at column is null.

Your code now needs to look as follows:

Limitations

Eloquent-only

In the same way that softDeletes is an Eloquent function and only works when you are using Eloquent and not when you are using QueryBuilder or raw DB, this package only maintains the deleted_at_uniqueable column when you are using Eloquent (though the SQL uniqueness will be enforced if you manually maintain this column in the same way that you would need to manually maintain the deleted_at column).

Database maintained uniqueness vs. Validation

This package prevents insertion of a duplicate undeleted record at an SQL database level. If you attempt to insert a duplicate record, an Illuminate\Database\QueryException will be thrown (which you can (obviously) catch if you wish).

For user requests to create or update a record, most developers will likely wish to ensure that the record will be unique at the request validation stage; this package can be used as an alternative or in addition to this sort of validation.

When restoring trashed records, or for requests not directly initiated by the user, the developer will need either to implement manual checks that the result will be unique or use this package and catch any QueryExceptions that result.

To validate the user request in the above example, you would typically have the following validation in your Requests:

Pre-softDeletes:

With softDeletes:

With softDeletes and softDeletesUnique:

Select performance

When you select (non-deleted) records using a SoftDeletes model, i.e. you don't use the withTrash() or onlyTrash() modifiers, Laravel's Eloquent automatically adds WHERE deleted_at IS NULL to the SELECT query. A request validation testing for uniqueness will likely generate a select statement like this under the covers. For these types of queries, to enable the database optimiser to avoid a full table scan you will likely still need some sort of index on deleted_at. Since we are now using deleted_at_uniqueable for the unique index, you may need to create a non-unique index on the deleted_at field as well, i.e. your migration would need to look like...

Installation & Usage

Installation

Once installed, softDeletesUnique support is automatically added to migration Blueprint objects.

Usage

In your Migrations...

  1. Add $table->softDeleteUnique()->after('deleted_at');
  2. Replace $table->unique(['deleted_at', 'column']); with $table->unique(['deleted_at_uniqueable', 'column']);
  3. Add a non-unique index on deleted_at with $table->softDeletes()->index(); or $table->index('deleted_at');.

In your Models...

  1. Add use Tranzakt\softDeletesUnique\Concerns\HasSoftDeletesUnique; to the header and use HasSoftDeletesUnique; to the top of the class.

As normal, you can use a parameter on the softDeletesUnique('deleted_at_str') to create the column with a different name, and use CONST DELETED_AT_UNIQUEABLE = 'deleted_at_str'; in your model to tell the model what the column name is.

How it works

This package has been written to use the standard Laravel Eloquent facilities as fully as possible.

The softDeletesUnique and dropSoftDeletesUnique methods are macroed into Blueprint.

$table->softDeletesUnique(); creates a new non-nullable string column deleted_at_uniqueable of up to 24 characters (format 'YYYY-MM-DD HH:MM:SS.xxxxxx'), that contains either '' when deleted_at is null, or a string representation if it is not null.

The HasSoftDeletesUnique trait creates observers on the creating, updating, deleting and restoring Eloquent actions and ensures that the deleted_at_uniqueable column is set appropriately.

And that's all folks.

Alternatives

This package is only one way to fix this database unique constraint issue, but it is believed to be the only common way of fixing it that works with all the Laravel supported RDBMS without change, and which doesn't require the coder to do any special DB:raw commands in the migrations.

However, depending on the RDBMS you are using, there are alternative solutions (including where necessary an additional index to support the WHERE deleted_at IS NULL added by softDeletes):

PostgreSQL / SQLite

Use 2 partial (filtered) indexes as follows:

Laravel Schema objects do not include the ability to define WHERE clauses on indexes, so you will need to use DB::raw to create and execute the above SQL data definition statements.

Because we have separate partial indexes when deleted_at is both NULL and NOT NULL, the database should be able to use one of these indexes when Eloquent's softDelete functionality adds WHERE deleted_at IS NULL to the select statement.

Microsoft SQL Server

Microsoft SQL Server considers NULL===NULL so that no special treatment is needed.

MySQL / MariaDB

Unfortunately neither MySQL nor MariaDB support indexes with WHERE clauses, and we need to use a "virtual column" instead.

The raw SQL needed to create a virtual column and index it is as follows:

I haven't tested this, however I am doubtful whether this index would be used for the WHERE deleted_at IS NULL clause, so a non-unique index on deleted_at will likely also be needed for performance (with other columns if the index would be more useful with them added).

The Laravel code for the above is:

License

This package is Licensed under the MIT open-source License.

Acknowledgements

This package has been built by standing on the shoulders of others who have done the hard work of identifying both the issue and the solution.

This package was originally authored by Sophist, with additional contributions from: .

If you submit a PR, please add your name to the above list as part of your PR.


All versions of laravel-softdeletesunique with dependencies

PHP Build Version
Package Version
Requires php Version ^8.0|^8.1|^8.2
laravel/framework Version ^8.0|^9.0|^10.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package tranzakt/laravel-softdeletesunique contains the following files

Loading the files please wait ....