Claim this agent
agentCLAWHUBUnverified

ia-php-laravel

Modern PHP 8.4 and Laravel patterns: architecture, Eloquent, migrations, queues, testing. Use when working with Laravel, Eloquent, Blade, artisan, or building/testing a framework-based PHP app. Not for php-src internals, standalone PHP libraries, or general PHP language discussion. Skill: ia-php-laravel Owner: iliaal Summary: Modern PHP 8.4 and Laravel patterns: architecture, Eloquent, migrations, queues, testing. Use when working with Laravel, Eloquent, Blade, artisan, or building/testing a framework-based PHP app. Not for php-src internals, standalone PHP libraries, or general PHP language discussion. Tags: latest:5.0.1 Version history: v5.0.1 | 2026-10-03T17:08:07.339Z | user v5.0.1 v5.0.0 |

OpenClaw

Rank

62

Safety

84

Downloads

2.7k

Updated

Oct 9, 2026

Version

5.0.1

Source

CLAWHUB

About

What it does, and when to use it.

Capability contract not published. No trust telemetry is available yet. 2.7K downloads reported by the source. Last updated 10/9/2026.

Avoid when

  • Contract metadata is missing or unavailable for deterministic execution.

Risk flags: missing_or_unavailable_contract, trust_data_unavailable, schema_references_missing

Public facts

Every fact links back to the source it came from.

Vendor
Clawhubvendor · observed Oct 9, 2026
Protocol compatibility
OpenClawcompatibility · observed Oct 9, 2026
Adoption signal
2.7K downloadsadoption · observed Oct 9, 2026
Latest release
5.0.1release · observed Oct 3, 2026
Handshake status
UNKNOWNsecurity

Install and run

Setup complexity: low.

clawhub skill install s17bcar8wq0xhegs0ny6f57ypd8484bw:compound-eng-php-laravel
  1. Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.
  2. Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data.

Contract: missing

curl -s "https://www.xpersona.co/api/v1/agents/clawhub-iliaal-compound-eng-php-laravel/snapshot"

Documentation

CLAWHUB

147,151 characters of source documentation, loaded on request.

Extracted files

5 files captured from the source.

SKILL.md

---
name: ia-php-laravel
class: language
description: >-
  Modern PHP 8.4 and Laravel patterns: architecture, Eloquent, migrations, queues, testing.
  Use when working with Laravel, Eloquent, Blade, artisan, or building/testing a
  framework-based PHP app. Not for php-src internals, standalone PHP libraries, or
  general PHP language discussion.
paths: "**/*.php"
---

# PHP & Laravel Development

Scoped to framework-level PHP. Work on php-src internals or a native PHP extension is C, not PHP: the `ia-c-systems` skill covers it, including the Zend API conventions (`gen_stub` arginfo, the request-scoped allocator, custom object handlers, `.phpt`).

## Working rules

- Keep simple CRUD simple; extract cross-model orchestration only when it has a concrete use.
- Validate and authorize at request boundaries; serialize through explicit resources and validate third-party responses.
- Preserve deployed migration history, queued payload compatibility, and concurrent writes.
- Verify cache compilation, queue execution, and HTTP behavior through their real entrypoints when those paths change.

## Code Style

- `declare(strict_types=1)` in every file
- Happy path last: guards and errors first, success at the end. Early returns, no `else`.
- Comments explain *why*, never *what*. Never comment tests. If code needs a "what" comment, rename or restructure.
- No single-letter variables: `$exception` not `$e`, `$request` not `$r`
- `?string` not `string|null`. Always specify `void`. Import classnames, never inline FQN.
- **Widening one parameter to `?T` obliges auditing every call site that forwards the same value**: the sibling call still declares `string`, and `null` throws a `TypeError` there even with no `declare(strict_types=1)`, because coercive mode never coerces `null` into a scalar. Strictness is decided by the file the CALL is written in, never by the callee's file. Full mechanism in [common-pitfalls.md](./references/common-pitfalls.md).
- Validation uses array notation `['required', 'email']` for easier custom rule classes
- PHPStan level 8+ (`phpstan analyse --level=8`); aim for 9 on new projects. `@phpstan-type` / `@phpstan-param` for generic collection types. The missing-iterable-value-type check lands at **level 6** (and every level above it), so any project at 8+ inherits it: use the generic form on every iterable (`@return Collection<int, User>`, `@param array<int, MyObject>`) and array-shape notation `array{first: SomeClass, second: SomeClass}` for fixed-key returns; a bare `Collection` or `array` will not clear it.


## Discipline

- Simplicity first: every change as simple as possible, minimal code impact
- Only touch what's necessary; no unrelated changes
- No hacky workarounds: if a fix feels wrong, step back and implement the clean solution
- New abstraction requires 3+ usage sites; otherwise inline it
- No empty catch blocks: log or rethrow, never swallow
- Verify before declaring done: `./vendor/bin/phpstan analyse --level=8 && ./vendor/bi

_meta.json

{
  "ownerId": "kn715jrbbh71q9zncr0bqdkr8n848q1a",
  "slug": "compound-eng-php-laravel",
  "version": "5.0.1",
  "publishedAt": 1791047287339
}

references/common-pitfalls.md

# Laravel Common Pitfalls: mechanism and fix

Mechanism and fix for the one-line entries in SKILL.md's Common Pitfalls list, plus the request-lifecycle and resource entries linked from Laravel Architecture and API Resources. Entries whose SKILL.md bullet links to `pitfalls-deep.md` are documented there instead; nothing is repeated across the two files.

## Model events and observers

### Query-builder update() bypasses the event layer

`Model::query()->where(...)->update([...])` and `Relation::update()` are query-builder writes: no model events fire, so observers, `Auditable` traits and `static::saving` / `static::updating` hooks are all bypassed. Anything those hooks enforce (an audit row, a search-index sync, a derived-column refresh) is silently void on that path. Fix: `lockForUpdate()` + `save()` inside a transaction keeps events firing; take the raw mass update only with an explicit `// intentionally bypasses <Observer>` comment naming what is skipped.

### FK cascades and the Eloquent event layer are different layers

`->cascadeOnDelete()` is a database constraint. The two failures are mirror images and both are silent.

**The cascade fires and the event layer does not.** The database removes the children itself, Eloquent never loads or deletes them, no `deleted` event fires, and no observer, `Auditable` trait, search sync or storage cleanup runs for them, while the parent's own delete IS audited, so the log looks populated and contains no record of what the cascade took with it. The tell in review: a sibling path in the same codebase deleting children row by row with a comment explaining why. That comment is the codebase saying it depends on model events, so every FK cascade in that family is a hole in whatever the events enforce.

**The cascade does not fire at all when the parent soft-deletes.** `SoftDeletes` intercepts `delete()` at the model layer and rewrites it as `UPDATE ... SET deleted_at = ...`; `ON DELETE CASCADE` only fires on real `DELETE` SQL. The parent row stays alive, the children's FK still points at a live row, and the cascade is a pure no-op for every path that calls `$parent->delete()`, usually the dominant one. It applies only to the `forceDelete()` minority, with no warning at migration time and no failure at runtime. Same trap in any ORM that overlays soft delete on an SQL referential action.

When a change adds a delete path on a parent, answer both: do the children die by cascade or row by row, and does the parent use `SoftDeletes`? `grep` the parent model for `use SoftDeletes;` and classify every `->delete()` / `->forceDelete()` call site. Delete per row inside the transaction wherever an event-layer invariant must hold. On the test side, write cascade assertions as `$parent->forceDelete()`: `forceDelete()` is defined on the base Eloquent `Model`, not only on the trait, so it is safe to write before `SoftDeletes` lands and stays green when the trait arrives from the target branch (verify at the pinned version rath

references/factories.md

# Factory Patterns

> When to read: when writing or refactoring Laravel model factories: basic shapes, states, sequences, relationships, and seed-vs-test boundaries.

## Basic Factory

```php
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title' => fake()->sentence(),
            'slug' => fake()->slug(),
            'content' => fake()->paragraphs(3, true),
            'published_at' => fake()->dateTimeBetween('-1 year', 'now'),
            'user_id' => User::factory(),
            'category_id' => Category::factory(),
        ];
    }
}
```

## States

Name states as adjectives or past participles; they describe what the model IS:

```php
public function unpublished(): static
{
    return $this->state(fn (array $attributes) => [
        'published_at' => null,
    ]);
}

public function published(): static
{
    return $this->state(fn (array $attributes) => [
        'published_at' => now(),
    ]);
}

// Usage
$post = Post::factory()->unpublished()->create();
```

## Relationships

```php
// Has many -- creates parent with 3 children
$post = Post::factory()
    ->has(Comment::factory()->count(3))
    ->create();

// Belongs to -- creates children for specific parent
$posts = Post::factory()
    ->count(3)
    ->for($user)
    ->create();

// Combined
$post = Post::factory()
    ->published()
    ->for($user)
    ->has(Comment::factory()->count(3))
    ->has(Tag::factory()->count(2))
    ->create();
```

## afterCreating Hooks

For side effects that require a persisted model:

```php
public function configure(): static
{
    return $this->afterCreating(function (Post $post) {
        $post->tags()->attach(
            Tag::factory()->count(3)->create()
        );
    });
}
```

## Sequences

```php
$users = User::factory()
    ->count(3)
    ->sequence(
        ['role' => 'admin'],
        ['role' => 'editor'],
        ['role' => 'viewer'],
    )
    ->create();
```

## Usage in Tests

```php
// Single model
$user = User::factory()->create();

// With overrides
$user = User::factory()->create(['email' => '[email protected]']);

// Multiple
$posts = Post::factory()->count(10)->create();

// In-memory (no DB write)
$user = User::factory()->make();
```

Always use `create()` for feature tests (persists to DB). Use `make()` only for unit tests that need a model instance without persistence.

## Factories build the model unguarded

`Factory::makeInstance()` wraps `new $model($attributes)` in `Model::unguarded(...)`, so a factory can set a column that `$fillable` would reject and `$guarded` would block. A non-fillable fixture attribute therefore needs a production-writer check; it is not proof of an unreachable row. `$fillable` governs mass assignment, while direct property assignment followed by `save()`, query-builder writes, observers, or database defaults can supply the same value.

For any test that pins a guard, a filter, or a "this column decides X" behaviour, compare the factory

references/feature-testing.md

# Feature Testing Patterns

> When to read: when writing Laravel feature tests for HTTP, auth, session, file upload, or other request-cycle scenarios.

## Authentication Testing

```php
public function test_authenticated_user_can_access_endpoint(): void
{
    $user = User::factory()->create();

    $this->actingAs($user)
        ->getJson('/api/profile')
        ->assertOk()
        ->assertJson(['data' => ['id' => $user->id]]);
}

public function test_guest_receives_401(): void
{
    $this->getJson('/api/profile')->assertUnauthorized();
}

// Sanctum with specific abilities
public function test_user_with_wrong_ability_gets_403(): void
{
    $user = User::factory()->create();
    Sanctum::actingAs($user, ['view-posts']);

    $this->postJson('/api/posts', ['title' => 'Test'])
        ->assertForbidden();
}
```

## Authorization Testing

```php
public function test_user_cannot_delete_others_posts(): void
{
    $user = User::factory()->create();
    $post = Post::factory()->create(); // different user

    $this->actingAs($user)
        ->deleteJson("/api/posts/{$post->id}")
        ->assertForbidden();
}

public function test_admin_can_delete_any_post(): void
{
    $admin = User::factory()->admin()->create();
    $post = Post::factory()->create();

    $this->actingAs($admin)
        ->deleteJson("/api/posts/{$post->id}")
        ->assertNoContent();

    $this->assertDatabaseMissing('posts', ['id' => $post->id]);
}
```

## Validation Testing

```php
public function test_post_requires_title_and_content(): void
{
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/api/posts', [])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['title', 'content']);
}
```

### assertJsonValidationErrors passes on ANY error for the field

`assertJsonValidationErrors(['phone'])` asserts only that `phone` appears as an errored key. It does not check which rule produced the message, and Laravel stops at the first failing rule for an attribute, so a fixture that trips an earlier format rule satisfies a test named `test_..._validates_unique_phone`. Deleting the rule under test leaves the test green, and the suite reads as coverage of a rule it never reaches.

The second source escapes a rule-chain read entirely: the competing rejection is application code throwing `ValidationException::withMessages(['field' => ...])` further down the request (a service-layer floor, a domain guard, a controller precondition). It is not in the FormRequest, so reading `rules()` finds nothing, and it lands on the identical key.

Three steps, in order:

1. Confirm the fixture would PASS every rule earlier in the chain than the one under test.
2. Delete the rule and re-run. Still green means the rule is not under test. Do this mechanically rather than by reading, whenever a `ValidationException` exists anywhere on the path.
3. Tighten to the message form (`assertJsonValidationErrors(['field' => 'must not be greater than'])`, which substr
Github ReposUpdated 1h agoRank 70

AionUi

Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!

MCPOPENCLAW
Github ReposUpdated 6mo agoRank 70

activepieces

AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents

OPENCLAW
Github ReposUpdated 6mo agoRank 70

cherry-studio

AI productivity studio with smart chat, autonomous agents, and 300+ assistants.

MCPOPENCLAW
Github ReposUpdated 7mo agoRank 70

CopilotKit

The Frontend for Agents & Generative UI. React + Angular

OPENCLAW

Machine-readable data

The same record, as JSON, for agents and crawlers.

{
  "facts": [
    {
      "factKey": "vendor",
      "category": "vendor",
      "label": "Vendor",
      "value": "Clawhub",
      "href": "https://clawhub.ai/iliaal/skills/compound-eng-php-laravel",
      "sourceUrl": "https://clawhub.ai/iliaal/skills/compound-eng-php-laravel",
      "sourceType": "profile",
      "confidence": "medium",
      "observedAt": "2026-10-09T12:03:04.663Z",
      "isPublic": true
    },
    {
      "factKey": "protocols",
      "category": "compatibility",
      "label": "Protocol compatibility",
      "value": "OpenClaw",
      "href": "https://www.xpersona.co/api/v1/agents/clawhub-iliaal-compound-eng-php-laravel/contract",
      "sourceUrl": "https://www.xpersona.co/api/v1/agents/clawhub-iliaal-compound-eng-php-laravel/contract",
      "sourceType": "contract",
      "confidence": "medium",
      "observedAt": "2026-10-09T12:03:04.663Z",
      "isPublic": true
    },
    {
      "factKey": "traction",
      "category": "adoption",
      "label": "Adoption signal",
      "value": "2.7K downloads",
      "href": "https://clawhub.ai/iliaal/compound-eng-php-laravel",
      "sourceUrl": "https://clawhub.ai/iliaal/compound-eng-php-laravel",
      "sourceType": "profile",
      "confidence": "medium",
      "observedAt": "2026-10-09T12:03:04.663Z",
      "isPublic": true
    },
    {
      "factKey": "latest_release",
      "category": "release",
      "label": "Latest release",
      "value": "5.0.1",
      "href": "https://clawhub.ai/iliaal/compound-eng-php-laravel",
      "sourceUrl": "https://clawhub.ai/iliaal/compound-eng-php-laravel",
      "sourceType": "release",
      "confidence": "medium",
      "observedAt": "2026-10-03T17:08:07.339Z",
      "isPublic": true
    },
    {
      "factKey": "handshake_status",
      "category": "security",
      "label": "Handshake status",
      "value": "UNKNOWN",
      "href": "https://www.xpersona.co/api/v1/agents/clawhub-iliaal-compound-eng-php-laravel/trust",
      "sourceUrl": "https://www.xpersona.co/api/v1/agents/clawhub-iliaal-compound-eng-php-laravel/trust",
      "sourceType": "trust",
      "confidence": "medium",
      "observedAt": null,
      "isPublic": true
    }
  ],
  "events": [
    {
      "eventType": "release",
      "title": "Release 5.0.1",
      "description": "v5.0.1",
      "href": "https://clawhub.ai/iliaal/compound-eng-php-laravel",
      "sourceUrl": "https://clawhub.ai/iliaal/compound-eng-php-laravel",
      "sourceType": "release",
      "confidence": "medium",
      "observedAt": "2026-10-03T17:08:07.339Z",
      "isPublic": true
    }
  ]
}

Record generated Oct 9, 2026.

Sponsored

Ads related to ia-php-laravel and adjacent AI workflows.