Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Entity Behavior Laravel Package

cycle/entity-behavior

Adds behavior attributes to Cycle ORM entities (UUID, timestamps, soft delete, optimistic lock, hooks, event listeners) plus an API to build custom behaviors. Use EventDrivenCommandGenerator when creating the ORM to enable event-driven commands.

View on GitHub
Deep Wiki
Context7
## Getting Started

### **Minimal Setup**
1. **Installation**
   ```bash
   composer require cycle/orm entity-behavior

Ensure your composer.json includes cycle/orm as a dependency (this package extends Cycle ORM's behavior).

  1. Basic Configuration Add the behavior to your entity definition in config/cycle.php:

    'behaviors' => [
        Cycle\Annotated\EntityBehavior::class,
        Cycle\EntityBehavior\EntityBehavior::class, // <-- Add this line
    ],
    
  2. First Use Case: Soft Deletes Annotate an entity to enable soft deletes:

    use Cycle\Annotated\Annotation\Column;
    use Cycle\EntityBehavior\Annotation\SoftDelete;
    
    #[SoftDelete]
    class User
    {
        #[Column(type: 'primary')]
        public int $id;
    
        #[Column(type: 'string')]
        public string $name;
    
        #[Column(type: 'datetime', nullable: true)]
        public ?string $deletedAt = null;
    }
    

    Now, User::delete() will set deletedAt instead of hard-deleting.


Implementation Patterns

Common Workflows

  1. Soft Deletes

    • Use #[SoftDelete] to enable soft deletes.
    • Query with ->whereDeletedAtIsNull() or ->withTrashed() (if using Cycle\ORM\Select\Query\Where\DeletedAt).
    • Restore with ->restore().
  2. Timestamps

    • Add #[Timestamp] to auto-manage createdAt/updatedAt:
      #[Timestamp]
      class Post
      {
          #[Column(type: 'datetime')]
          public string $createdAt;
      
          #[Column(type: 'datetime')]
          public string $updatedAt;
      }
      
  3. Sluggable Fields

    • Use #[Sluggable] to auto-generate slugs:
      #[Sluggable(['name'], 'slug')]
      class Product
      {
          #[Column(type: 'string')]
          public string $slug;
      }
      
  4. Observers

    • Attach behaviors via Cycle\EntityBehavior\Observer:
      use Cycle\EntityBehavior\Observer;
      
      class UserObserver extends Observer
      {
          public function beforeSave(User $user): void
          {
              $user->updatedAt = now();
          }
      }
      
    • Register in config/cycle.php:
      'observers' => [
          User::class => UserObserver::class,
      ],
      
  5. Optimistic Locking with Sequential Persist

    • Ensure #[OptimisticLock] works correctly with sequential persists (fixed in 1.7.1):
      use Cycle\EntityBehavior\Annotation\OptimisticLock;
      
      #[OptimisticLock]
      class Product
      {
          #[Column(type: 'integer')]
          public int $version = 1;
      }
      
    • This resolves race conditions during concurrent updates.

Integration Tips

  • Query Builder Extensions Use Cycle\EntityBehavior\Query\Where\DeletedAt for soft-delete queries:

    $query->whereDeletedAtIsNull(); // Only active records
    $query->whereDeletedAtIsNotNull(); // Only trashed records
    
  • Migrations Ensure your migration matches the behavior’s expected schema (e.g., deletedAt column for soft deletes, version for optimistic locking).

  • Testing Mock behaviors in tests by overriding methods or using Cycle\ORM\Select\Query\Where\DeletedAt directly. For optimistic locking, test concurrent updates to verify version increments.


Gotchas and Tips

Pitfalls

  1. Schema Mismatch

    • If deletedAt or version (for optimistic locking) is missing, behaviors will fail silently. Always include required columns in migrations.
  2. Observer Order

    • Observers run in registration order. Place critical logic first in config/cycle.php.
  3. Sluggable Conflicts

    • Ensure #[Sluggable] fields are unique in the database to avoid collisions.
  4. Soft Delete Queries

    • Forgetting ->whereDeletedAtIsNull() will return both active and trashed records.
  5. Optimistic Locking Race Conditions

    • If #[OptimisticLock] fails during concurrent updates, ensure your application handles Cycle\ORM\Exception\OptimisticLockException gracefully.

Debugging

  • Enable Logging Add 'debug' => true to config/cycle.php to log behavior events.

  • Check Behavior Events Use Cycle\EntityBehavior\Event to trace execution:

    $event = new Cycle\EntityBehavior\Event\BeforeSave($entity);
    $event->dispatch(); // Debug before dispatch
    
  • Optimistic Lock Debugging Enable SQL logging to verify version checks:

    'debug' => [
        'sql' => true,
    ],
    

Extension Points

  1. Custom Behaviors Extend Cycle\EntityBehavior\Behavior to create reusable logic:

    class AuditBehavior extends Behavior
    {
        public function beforeSave(EntityInterface $entity): void
        {
            $entity->auditLog[] = new AuditLog();
        }
    }
    
  2. Override Defaults Modify behavior logic by extending annotations (e.g., #[SoftDelete(useCurrentTimestamp: false)]).

  3. Query Extensions Add custom Where clauses by implementing Cycle\ORM\Select\Query\Where\WhereInterface.

Config Quirks

  • Behavior Priority Later behaviors in config/cycle.php override earlier ones. Order matters for conflicts.

  • Annotation Caching Clear vendor/compiled after adding new annotations to avoid stale behavior.

  • Optimistic Locking Fix (1.7.1) The sequential persist issue with #[OptimisticLock] is now resolved. Ensure your tests cover concurrent update scenarios to validate the fix.


NO_UPDATE_NEEDED would not apply here due to the critical fix in 1.7.1. The assessment has been updated to reflect the new behavior and include testing/debugging tips for optimistic locking.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
cocosmos/filament-sticky-save-bar
patrickbussmann/oauth2-apple
3brs/enterprise-security-bundle
anousss007/vigilance
supportpal/eloquent-model
ardenexal/fhir-models
laravel-at/laravel-image-sanitize
romalytar/yammi-audit-log-laravel
ardenexal/fhir-validation
arshaviras/weather-widget
laravel-chronicle/core
sunchayn/nimbus
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope