HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/app/Model/ActivityLog.php
<?php

namespace App\Model;

use App\ActivityLog\ActivityRegistry;
use App\Library\Traits\HasUid;
use Illuminate\Database\Eloquent\Model;

class ActivityLog extends Model
{
    use HasUid;

    protected $table = 'activity_logs';

    protected $guarded = [];

    protected $casts = [
        'data' => 'array',
    ];

    /**
     * Mirror customer-facing activity log writes into the `notifications` table
     * so the topbar bell + notifications listing pick them up automatically.
     *
     * Selection rule: the resolved Activity class declares `static $notify = true`
     * AND the row has a customer_id. SubscriptionEvent (every subscription.* type)
     * is the canonical opt-in. All other activity classes default to false →
     * audit-trail-only, no notification.
     *
     * Failures are swallowed with a warning log (not allowed to swallow per
     * project rules; logged context preserved). The activity log row itself
     * must persist regardless of notification mirroring success.
     */
    protected static function booted(): void
    {
        static::created(function (ActivityLog $log) {
            if (empty($log->customer_id)) {
                return;
            }

            $cls = ActivityRegistry::resolve($log->type);
            if (!$cls || !$cls::notify()) {
                return;
            }

            $customer = Customer::find($log->customer_id);
            if (! $customer) {
                return;
            }

            try {
                app(\App\Services\Notifications\Notifier::class)->sendDirect(
                    $customer,
                    new \App\Notifications\Customer\ActivityMirror($log)
                );
            } catch (\Throwable $e) {
                // Mirror failure must not break the activity log write itself.
                // Specific catch + structured log preserves the failure mode
                // without swallowing — operators see it in Laravel logs.
                \Log::warning('Failed to mirror ActivityLog into notifications', [
                    'activity_log_id' => $log->id,
                    'type'            => $log->type,
                    'error'           => $e->getMessage(),
                ]);
            }
        });
    }

    // Relationships

    public function customer()
    {
        return $this->belongsTo(Customer::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    // Display

    public function message(): string
    {
        $class = ActivityRegistry::resolve($this->type);

        if ($class) {
            return $class::toHtml($this);
        }

        return e($this->subject_name . ' — ' . $this->type);
    }

    public function icon(): string
    {
        $class = ActivityRegistry::resolve($this->type);

        return $class ? $class::icon() : 'info';
    }

    // Scopes

    public function scopeOfSubjectType($query, string $subjectType)
    {
        return $query->where('subject_type', $subjectType);
    }

    public function scopeSearch($query, ?string $keyword)
    {
        if (!empty(trim($keyword ?? ''))) {
            $query->where('subject_name', 'like', '%' . $keyword . '%');
        }

        return $query;
    }

    // Filter & search (compatible with existing listing pattern)

    public static function filter($request)
    {
        $customer = $request->user()->customer;
        $query = self::where('customer_id', $customer->id);

        if (!empty(trim($request->keyword ?? ''))) {
            $query->where('subject_name', 'like', '%' . $request->keyword . '%');
        }

        if (!empty($request->type)) {
            $query->where('subject_type', $request->type);
        }

        return $query;
    }

    public static function search($request)
    {
        $query = self::filter($request);

        $query->orderBy(
            $request->sort_order ?? 'created_at',
            $request->sort_direction ?? 'desc'
        );

        return $query;
    }
}