AssertableJsonApi::data(Closure) — scope into the data member using the full AssertableJson fluent API (where, has, missing, count, etc, dd, dump, when, tap, …)AssertableJsonApi::meta(Closure) — scope into metaAssertableJsonApi::links(Closure) — scope into linksAssertableJsonApi::errors(Closure) — scope into errorsAssertableJsonApi::included(Closure) — scope into includedAssertableJsonApi::relationship(string $name, Closure) — scope into relationships.{name}.data of the current resourceAssertableJsonApi::at(int $position, Closure $callback) — optional closure variant; when a callback is supplied the method runs assertions inside that scope and returns $this (the collection) for further chainingAssertableJsonApi::atRelation(Model $model, Closure $callback) — optional closure variant on atRelationAssertableJsonApi::assertIsJsonApiDocument() — validates the top-level JSON:API document structure (requires at least one of data/errors/meta; data and errors must not coexist; included requires data)errors, no data) are now accepted by fromTestResponse and can be asserted via errors()AssertableJsonApi constructor is now aligned with the parent AssertableJson(array $props, ?string $path = null). All JSON:API state (id, type, attributes, relationships, included) is now derived from props via accessors instead of stored as separate fields. As a result, parent methods scope(), first(), each(), where(), has(), missing(), count(), etc(), dd(), dump(), when(), tap() are now fully functional on AssertableJsonApi instances.fromTestResponse no longer silently collapses a collection response to the first item. Calling hasAttribute(), hasId(), hasType(), or relationship methods directly on a collection root now operates on the root document, not the first item. Use ->at(0) to scope into a specific collection item before running those assertions.hasAttribute($name, $value) value comparison tightened. The old implementation used assertContains($value, $attributes) which matched the value against any attribute — regardless of key. The new implementation uses assertSame($value, $attributes[$name]) which correctly checks the exact key/value pair. Action required: any test that relied on the old loose matching (e.g. hasAttribute('subtitle', 'Hello') passing because a different key happened to hold 'Hello') needs to be corrected.hasAttribute($name, $value) skipping falsy values fixed. The old guard was if ($value), which skipped the value check when $value was 0, false, or ''. The guard is now if ($value !== null), so values of 0 and false are correctly asserted.toArray() now returns the full document props (delegating to AssertableJson::toArray()) instead of only the attributes of the current resource. Code that called ->toArray() to obtain attributes should use ->toArray()['data']['attributes'] or ->prop('data.attributes').hasNotAttribute($name, $value) semantics clarified. Without $value, asserts the attribute key is absent. With $value, asserts that either the key is absent or the key's value differs from $value. Previously the method always failed if the key existed, even when only checking that a specific value was not present.hasAttributes() now accepts both list form (['title', 'abstract'] — key existence only) and map form (['title' => 'Hello'] — key + exact value).scope(), first(), each() from the parent now work correctly because the constructor matches the parent signatureAssertableJsonApi::rootProps is threaded into child scopes so included, meta, and links are reachable from any scope depthApiable::modelResourceMap() to register custom JsonApiResource classes per model globallyApiable::jsonApiResourceFor() to resolve the registered resource class for a given model instanceJsonApiResponse::usingResource() to override the resource class used for serialization per-responseJsonApiResource::toApplicationJsonArray() for plain JSON serialization merging model attributes with computed onesJsonApiResource classarray_walk callback was not returning the modified array); replaced with an explicit loop/?filter[withStatuses]=Active&filter[withStatuses]=Archived) (#15)apiable:docs command to generate API documentation from PHP attributes in Postman v2.1, Markdown/MDX, and OpenAPI 3.1 formats$description property to all *QueryParam attributes for documentation generation#[DocumentedResource], #[DocumentedEndpointSection], and #[EndpointResource] PHP attributes for annotating controllersOpenSoutheners\LaravelApiable\Documentation\Generator class for building resource documentation trees from registered routesOpenSoutheners\LaravelApiable\Http\JsonApiPaginator class exposing pagination logic as a typed static methodHandler::includesTrace() was calling env('APP_DEBUG') outside of the config directory, which returns null in production; now uses config('app.debug')IteratesResultsAfterQuery had leaked application-specific imports (App\Domain\Process\Models\Box\Box, App\Models\ModelForm) left over from a real projectaddAppendsToResult() instanceof JsonApiCollection check was unreachable because it was nested inside an instanceof JsonApiResource branch (since JsonApiCollection extends JsonApiResource); logic restructured to check the most-specific subtype firstaddAppendsToResult() instanceof Paginator branch was using the Paginator contract which does not declare through(); changed to AbstractPaginatorCollectsResources::collects() was missing a return null fallback, causing PHPStan to report a missing return statementisScope() in ApplyFiltersToQuery was using get_parent_class() to exclude base builder methods, which could return false for unknown types; now diffs against Illuminate\Database\Eloquent\Builder::class directly, which is also more accurateGenerator (malformed class name OpenSouthenersLaravelApiableDocumentationResource → \OpenSoutheners\LaravelApiable\Documentation\Resource)FastPaginateApiable::toJsonApi() given collection as input returns empty responsejsonApiPaginate() method typosjsonApiPaginate() methodopen-southeners/laravel-helpers with open-southeners/extended-phpJsonApiResponseOpenSoutheners\LaravelApiable\Http\JsonApiResponseOpenSoutheners\LaravelApiable\Http\RequestQueryObjectOpenSoutheners\LaravelApiable\Http\Resources\JsonApiResourceOpenSoutheners\LaravelApiable\Http\Resources\JsonApiCollectionOpenSoutheners\LaravelApiable\Contracts\ViewQueryableOpenSoutheners\LaravelApiable\Contracts\ViewableBuilderHandler::withHeader (used as apiable()->jsonApiRenderable()->withHeader) to add headers to JSON errorstags_count (needs to end with _count). E.g:JsonApiResponse::using(Post::class)->allowInclude(['tags_count']);
// Then request this with ?include=tags_count
self types in traits (replaced to static)open-southeners/laravel-helpers utilities package version constrain so other packages can use similar versions under the same major releasewithCount method usage with apiable is replacing the underlying added selects for these countsresponse.formatting with type and force sub-items as optionsApiable::forceResponseFormatting())ForceAppendAttribute when reading controller attributesJsonApiResponse::applyDefaultSort method or OpenSoutheners\LaravelApiable\Attributes\ApplyDefaultSort attributeJsonApiResponse::applyDefaultFilter method or OpenSoutheners\LaravelApiable\Attributes\ApplyDefaultFilter attributeJsonApiResponse::forceFormatting methodRequest::wantsJsonApi() only looks at Accept header, not looking for Content-Type one anymoreAccept header if they want a diffent response formatting. For e.g. sending Accept: application/json will get raw JSON response. Default being configured in the config/apiable.php make sure to publish or update it/posts?sort=tags.created_at)JsonApiResponse using viewable builder (query scopes to filter by view permission) didn't send any user as first parameterHow can I help you explore Laravel packages today?