All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning and this changelog format.
- Allow
laravel-json-api/core
v4 and v5.
- Remove deprecation notices in PHP 8.4.
- #41 Handle key column not existing in cursor paginator.
- #38 Added
WhereAll
andWhereAny
filters.
- #39 Fixed a bug in the eager loader iterator where include
paths starting with the same word were incorrectly removed. E.g.
car
andcarOwner
would result in justcarOwner
.
- #37 Add Eloquent cursor pagination implementation.
- #36 Support Eloquent dynamic relationships.
- Package is now licensed under the MIT license.
- BREAKING Package now requires Laravel 11.
- Minimum PHP version is now
8.2
. - Use
assert()
within fillable relation field classes rather as an optimisation. - #34 BREAKING The soft delete driver now throws an exception if the model is not successfully soft-deleted. This can happen if a listener or observer on the model aborts the delete operation. Previously the driver just carried on, which was incorrect. The exception has to be thrown in this scenario because we are now in an invalid state - what the client requested and what actually happened does not match up. If developers want to avoid this scenario, they should use authorization or validation logic so that clients get an error response that explains why the soft delete cannot be fulfilled.
- #31 BREAKING Use
self
as return type on Eloquent query classes. This is potentially breaking any of these classes have been extended.
- #30 Allow a wider range of Eloquent relations in
the
QueryToMany
andQueryToOne
classes. This means packages like culturegr/custom-relation will work with this package.
- #29 Avoid unnecessary query when there is no need to count a relationship.
- Upgraded to Laravel 10 and set minimum PHP version to
8.1
.
- New
MultiPaginator
that allows a schema to offer multiple different pagination strategies.
- laravel#223 Ensure a model always has fresh data from the database after a write operation, to prevent stale data on cached relationships.
- #27 Added the
WhereNull
andWhereNotNull
filters.
- Pass sparse field sets to the
JsonApiBuilder
class, ensuring that they are present on any generated page objects. Previously this omission meant that page URLs were missing any fields sent by the client.
- The
Number
field can now be configured to accept numeric strings by calling theacceptStrings()
method on the field.
- The
JsonApiBuilder
class was previously converting anull
decoded id to an empty string when querting for a resource id. This has been fixed to passnull
to the query builder instead of the empty string, as this was most likely the cause of failures in Postgres.
- Added support for PHP 8.1.
- Added support for Laravel 9.
- Added return types for internal methods, to remove deprecation warnings on PHP 8.1.
- #20 BREAKING To support PHP 8.1 we needed to rename the
ReadOnly
contract and trait. This is because PHP 8.1 introducedreadonly
as a reserved word. The following changes were made:LaravelJsonApi\Eloquent\Contracts\ReadOnly
is nowIsReadOnly
.LaravelJsonApi\Eloquent\Fields\Concerns\ReadOnly
is nowIsReadOnly
.
- The maximum PHP version is now 8.0. This is because this package does not work in its current form with PHP 8.1. The next major version of this package will support PHP 8.1.
- laravel#139 Fix the
WhereHas
andWhereDoesntHave
filters that have been broken since1.0.0
. Previously they have been iterating over filters on the schema to which the relationship belongs - which is incorrect. They now correctly iterate over the filters on the schema on the other side of the relationship (this inverse filter).
- #16 New filter classes that allow you to filter via a
relationship's filters:
Has
WhereHas
WhereDoesntHave
- #18 Extracted some filter code to the
HasColumn
andHasOperator
traits. - Extracted logic to apply sort and filter parameters to an Eloquent query builder into separate classes:
FilterApplicator
andSortApplicator
.
- BREAKING Countable relationships are now not countable by default. This change has been made as the countable feature is not considered production ready as we plan to make breaking changes to the implementation. By changing the default setting to off, you now have to opt-in to this experimental feature.
- Update the
SoftDeleteDriver
to useclass_uses_recursive
to check if the model support soft-deleting. - #15 Change the
Scope::make()
method to usestatic
instead ofself
. - Moved the following into the
QueryBuilder
namespace. This change should not affect consuming applications as these classes are meant for internal package use:JsonApiBuilder
class.ModelLoader
class.Aggregates
namespace.EagerLoading
namespace.
- #14 Allow a
null
value in the filterHasDelimiter
trait. - When detecting if a query needs a deterministic order, the page paginator will now also correctly match the qualified primary key of the model. Previously only the unqualified column name was matched. In MySql this led to the deterministic order overriding an existing descending sort for the primary key.
- The cursor pagination implementation has been moved to a separate package:
laravel-json-api/cursor-pagination. This is so that we can
add support for Laravel's new cursor implementation within this Eloquent package. To migrate, install the new package
and then change your import statements from
LaravelJsonApi\Eloquent\Pagination\CursorPagination
toLaravelJsonApi\CursorPagination\CursorPagination
.
- Developers can now fully control the extraction of attribute values from a model by providing a closure to the
extractUsing()
method on attributes. This callback receives the model, the column name, and the serialized value. Resource classes are still the recommended way of fully customising serialization of models to JSON:API resource objects. However, theextractUsing()
method is useful where a developer only needs to customise one or two attribute values on a resource.
- #13 The default order by column in the page paginator now has the table name added. This fixes problems with pagination on relationships or other joins.
- Updated the
Pagination\ProxyPage::withQuery()
method to remove iterable type-hint that has been removed from the page interface. The class was also madefinal
, as it is not intended to be extended. Although these changes are technically breaking, they are unlikely to affect consuming applications.
- The
JsonApiBuilder
was incorrectly castingnull
to an include paths object. On pages, this would incorrectly result in pagination links having aninclude=
(empty) parameter. This has been fixed, so include paths will only be set on the pagination links if include paths were actually specified.
- Schemas now support additional sort field classes, that define how to sort models using sort fields that are not
attributes. Sort field classes must implement the new
SortField
contract. Three initial sort classes are available:SortColumn
,SortCountable
andSortWithCount
. - Default sort order for resources can now be defined on the Eloquent schema using the
$defaultSort
property.
- #10 The
HasMany
field can now handle detaching models from the relationship in three ways. Either it will set the inverse relationship tonull
(the default behaviour), or it can delete the related models using either theModel::delete()
orModel::forceDelete()
methods. The default behaviour matches the behaviour in previous versions, so this change is non-breaking. The behaviour can be configured via thekeepDetachedModels()
,deleteDetachedModels()
andforceDeleteDetachedModels()
methods. - The
HasOne
field can now handle detaching a related model from the relationship in three ways. Either it will set the inverse relationship columns tonull
(the default behaviour), or it can delete the related model using eitherModel::delete()
orModel::forceDelete()
. The default behaviour matches the behaviour in previous versions, so this change is non-breaking. The behaviour can be configured via thekeepDetachedModel()
,deleteDetachedModel()
andforceDeleteDetachedModel()
methods.
- When using the
Attribute::fillUsing()
method to customise filling an attribute value into a model, the closure now receives the entire validated data as its fourth argument. This allows the closure to use other attributes when calculating the value to fill into the model. - Attribute fields now support the columns being on related models, allowing resources to serialize related models as
attributes rather than relationships. This is primarily intended for use with Eloquent
belongsTo
,hasOne
,hasOneThrough
andmorphOne
relations that can have default related models. As part of this feature, the model hydrator will now iterate through loaded relations on the model and save any models that are dirty. - Schemas that have attribute fields with values derived from related models will automatically eager load the relationship by adding the relationship to the default eager load paths for the schema.
- BREAKING The
Contracts\Fillable::fill()
method now expects the entire validated data as its third argument. - BREAKING The
Contracts\Fillable
interface now has amustExist()
method. This allows an attribute to indicate that the primary model being filled must exist in the database before the attribute is filled. This is intended for use by attributes that fill related models. - BREAKING The
Contracts\FillableToOne
andFillableToMany
interfaces now no longer extend theFillable
interface. This is so that thefill()
methods can correctly type-hint the related identifier(s) that are expected when filling a relationship. Effectively theFillable
contract is now intended for use by theid
field and attribute fields.
- To-many relationships are now countable. This allows a client to specify, via a query parameter, which relationships
it wants to be counted. These are used by the implementation to load counts on the Eloquent model, so that the count
values can be added to the relationship's
meta
member. Refer to documentation for implementation details. - Package now supports encoding of resource IDs. Resource IDs are correctly decoded when querying the database for matching models.
- To support ID encoding, the following filters have been added specifically for filtering by resource ids:
WhereIdIn
WhereIdNotIn
- Made improvements to the eager loading implementation. All classes in the
EagerLoading
namespace are now marked as internal, as they are not intended for use outside of this package. The public API isJsonApiBuilder::with()
,ModelLoader::load()
andModelLoader::loadMissing()
. - Refactored the Eloquent Schema
loader()
method toloaderFor($modelOrModels)
. - The
JsonApiBuilder
class now expects the schema container as its first argument. To construct a newJsonApiBuilder
instance, theSchema::newQuery()
andRelation::newQuery()
methods should be used.
- BREAKING Deleted the
Pagination\Concerns\HasPageMeta
trait as the trait is now in thelaravel-json-api/core
package asLaravelJsonApi\Core\Pagination\Concerns\HasPageMeta
.
- #6 Package now fully supports soft-deleting resources. If a
model allows soft deleting, but no changes are made to a schema, then deleting the resource will soft-delete it and
that resource will no longer appear in the API. However, if soft-delete capability is to be exposed to the client, a
schema should apply the
SoftDeletes
trait from this package and add aFields\SoftDelete
field to their list of fields. Refer to documentation for full list of capabilities. - Added the
WithTrashed
andOnlyTrashed
filter classes. - The package now supports multi-resource models. This feature allows a model to be represented as more than one JSON: API resource class and works by having proxy classes for each additional representation of a model. Refer to documentation for examples and details of how to implement multi-resource models.
- #7 Added a new
MorphToMany
JSON:API relation field. This wraps several sub-relation fields and presents them as a single polymorphic relationship. The relationship value works both as thedata
member of the relationship object and as a relationship end-point. The relationship is modifiable when every sub-relation is writeable (implements theFillableToMany
relation) and each resource type that can be in the relationship maps to a single sub-relation. Include paths also work, with the include paths only being applied to the sub-relations for which they are valid.
- BREAKING Deleting a model now uses
Model::delete
instead ofModel::forceDelete
. This change was required when adding full support for soft-deleting resources. - BREAKING Repositories are now injected with a driver which defines the database interactions for the repository. This allows database interactions to be modified, without having to rewrite the repository class - and is used as to implement the soft-deletes feature.
- BREAKING The
sync
,attach
anddetach
methods on theFillableToMany
interface now type-hintiterable
as their return type. Previously they type-hinted the Eloquent collection class. - BREAKING The eager load implementation has been modified to support the new polymorphic to-many relation.
Generally this should not cause any breaking changes, because the eager loading classes were effectively used
internally to handle eager loading. Changes include removing the
skipMissingFields
methods (that existed in multiple locations) and rewriting theEagerLoadPath
class.
- BREAKING Remove the following methods from the
Schema
class. These were originally added as convenience methods if writing custom controller actions - however, their use is now not suitable as all database querying should be executed via the repository class to ensure Eloquent query builders are created according to the database driver that is in use. The methods are:Schema::newQuery()
Schema::query()
- The Eloquent schema now has
indexQuery
andrelatableQuery
methods. These allow filtering for authorization purposes when a list of resources is being retrieved. For instance, it could filter those queries so that only models belonging to the authenticated user are returned. - Can now determine whether multiple filters should return zero-to-one resource using the
Schema::isSingular()
method.
- BREAKING The query builder classes have been updated for changes to the interfaces they implement. This adds
the
withRequest()
method and renamesusing()
towithQuery()
. - #2 BREAKING The
Fillable
contract now type-hints the request class in its read-only method signatures, and allows it to benull
. TheReadOnly
trait has been updated, so this is unlikely to affect field classes if the trait has been used. - BREAKING If no request class is provided to the
ModelHydrator
class via its newwithRequest()
method, it is now assumed the hydration is occurring outside of a HTTP request. I.e. that the developer is manually triggering the hydration. Without the HTTP request, fields will not be checked for their read-only state and will be filled if the provided data has a value for the field. Implementing libraries must ensure thatwithRequest()
is called when filling values provided by a HTTP client. - BREAKING Renamed the
Builder
class toJsonApiBuilder
. This change was made as it was confusing what aBuilder
referred to, because Laravel uses this class name for Eloquent builders. - The
Relation::type()
method should now be used to set the inverse resource type on a relationship field.
- The
Builder::filters()
method now correctly yields both the schema's filter and the filters from a relationship, if one is set. Previously the filters were not yielded correctly if there was a relationship. - the
QueryToOne
andQueryToMany
builders now correctly use the model's relation name instead of the JSON:API field name when retrieving the relationship object from the model. Previously this would have failed with an error if the model relationship name was not the same as the JSON:API field name.
- The
Relation::inverseType()
method is deprecated and will be removed in1.0-stable
. UseRelation::type()
instead.
- Relationship fields now implement the new
isValidated()
method, indicating whether the field value should be merged with client provided values for an update request. By default, theBelongsTo
andMorphTo
relations are validated, whereas all other fields are not. This is a sensible default, as theBelongsTo/MorphTo
identifiers are stored on the model so are likely to be required for validation. The defaults can be overidden on the fields using themustValidate()
ornotValidated()
methods. - Eager loading now supports schemas having default eager load paths. This is set via the
$with
property on the schema, which is returned by the publicwith()
method.
- Moved the existing
EagerLoader
,EagerLoadMorphs
andEagerLoadPath
to theEagerLoading
namespace.
- The
BelongsToMany
field now correctly yields both its own filters and filters from its pivot fields. Previously the filters were not yielded correctly if both the field and pivot had filters.
- #1 Eloquent fields now support serializing models to JSON values. This means that resource classes now become optional: because in the absence of a resource class, the implementation can fall-back on serializing resources using the Eloquent schema.
- BREAKING Split the
Arr
field class into two:ArrayList
andArrayHash
. This was required because now that the fields are also serializing values, the handling of empty values is different depending on whether it is a list (empty array) or a hash (empty array converted tonull
). - Can now set the URI field name for a relationship on the schema's relationship field, using the
withUriFieldName()
method. Alternatively, theretainFieldName()
method can be used to retain the field name as-is.
Initial release.