File: /home/xedaptot/ai.naniguide.com/app/Model/Funnel.php
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use App\Library\Traits\HasUid;
use Validator;
/**
* @property \App\Model\MailList|null $mailList
*/
class Funnel extends Model
{
use HasFactory;
use HasUid;
use SoftDeletes;
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
protected $fillable = [
'name', 'slug', 'description', 'mail_list_id', 'settings', 'tags',
];
protected $casts = [
'settings' => 'array',
'is_template' => 'boolean',
'published_at' => 'datetime',
];
// -------------------------------------------------------------------------
// Boot
// -------------------------------------------------------------------------
protected static function boot()
{
parent::boot();
static::creating(function ($funnel) {
if (empty($funnel->slug) && $funnel->name) {
$funnel->slug = $funnel->generateUniqueSlug($funnel->name);
}
});
}
// -------------------------------------------------------------------------
// Relationships
// -------------------------------------------------------------------------
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function steps()
{
return $this->hasMany(FunnelStep::class)->orderBy('sort_order');
}
public function mailList()
{
return $this->belongsTo(MailList::class, 'mail_list_id');
}
public function products()
{
return $this->belongsToMany(FunnelProduct::class, 'funnel_product_links')
->withPivot('sort_order', 'custom_price')
->withTimestamps()
->orderBy('funnel_product_links.sort_order');
}
public function productLinks()
{
return $this->hasMany(FunnelProductLink::class);
}
public function domains()
{
return $this->hasMany(FunnelDomain::class);
}
// -------------------------------------------------------------------------
// Scopes
// -------------------------------------------------------------------------
public function scopeOfCustomer($query, $customer)
{
return $query->where('customer_id', $customer->id);
}
public function scopePublished($query)
{
return $query->whereNotNull('published_at');
}
public function scopeDraft($query)
{
return $query->whereNull('published_at');
}
public function scopeTemplate($query)
{
return $query->where('is_template', true);
}
public function scopeNotTemplate($query)
{
return $query->where('is_template', false);
}
public function scopeSearch($query, $keyword)
{
if ($keyword) {
$query->where('name', 'like', '%' . trim($keyword) . '%');
}
}
// -------------------------------------------------------------------------
// Publish / Draft
// -------------------------------------------------------------------------
public function publish()
{
$this->published_at = now();
$this->save();
}
public function unpublish()
{
$this->published_at = null;
$this->save();
}
public function isPublished(): bool
{
return !is_null($this->published_at);
}
public function isDraft(): bool
{
return is_null($this->published_at);
}
// -------------------------------------------------------------------------
// Factory / CRUD helpers
// -------------------------------------------------------------------------
public static function newDefault($customer)
{
$funnel = new self();
$funnel->customer_id = $customer->id;
$funnel->status = self::STATUS_ACTIVE;
return $funnel;
}
public function fillParams($params)
{
$this->name = $params['name'] ?? $this->name;
$this->description = $params['description'] ?? $this->description;
$this->tags = $params['tags'] ?? $this->tags;
if (!empty($params['slug'])) {
$this->slug = $params['slug'];
}
if (array_key_exists('mail_list_uid', $params)) {
if (!empty($params['mail_list_uid'])) {
$this->mail_list_id = MailList::findByUid($params['mail_list_uid'])->id;
} else {
$this->mail_list_id = null;
}
}
// Merge settings (preserve existing keys not in this request)
$settings = $this->settings ?? [];
$settingsKeys = [
'favicon', 'seo_title', 'seo_description', 'og_image',
'facebook_pixel_id', 'ga_measurement_id', 'gtm_container_id',
'webhook_url', 'webhook_events',
'head_scripts', 'body_scripts',
'checkout_test_mode',
];
foreach ($settingsKeys as $key) {
if (array_key_exists("settings_{$key}", $params)) {
$settings[$key] = $params["settings_{$key}"];
}
}
$this->settings = $settings;
}
public function saveFromParams($params)
{
$this->fillParams($params);
$validator = Validator::make($params, [
'name' => 'required|max:255',
]);
if ($validator->fails()) {
return $validator;
}
$this->save();
return $validator;
}
// -------------------------------------------------------------------------
// Slug
// -------------------------------------------------------------------------
public function generateUniqueSlug($name)
{
$slug = Str::slug($name);
$original = $slug;
$count = 1;
while (self::withTrashed()
->where('customer_id', $this->customer_id)
->where('slug', $slug)
->where('id', '!=', $this->id ?? 0)
->exists()
) {
$slug = $original . '-' . $count++;
}
return $slug;
}
// -------------------------------------------------------------------------
// URL / Entry
// -------------------------------------------------------------------------
public function getPublicUrl()
{
return url('f/' . $this->slug);
}
public function getEntryStep()
{
return $this->steps()->where('is_published', true)->orderBy('sort_order')->first();
}
// -------------------------------------------------------------------------
// Duplicate / Clone
// -------------------------------------------------------------------------
public function duplicate()
{
$copy = $this->replicate(['uid', 'slug', 'published_at', 'deleted_at']);
$copy->name = $this->name . ' (Copy)';
$copy->slug = $copy->generateUniqueSlug($copy->name);
$copy->published_at = null;
$copy->save();
// Clone steps with templates
foreach ($this->steps as $step) {
$step->duplicateTo($copy);
}
// Clone product links
foreach ($this->productLinks as $link) {
$newLink = $link->replicate();
$newLink->funnel_id = $copy->id;
$newLink->save();
}
return $copy;
}
public static function cloneFrom($templateFunnel, $customer)
{
$funnel = $templateFunnel->replicate(['uid', 'slug', 'published_at', 'deleted_at', 'is_template']);
$funnel->customer_id = $customer->id;
$funnel->is_template = false;
$funnel->slug = $funnel->generateUniqueSlug($templateFunnel->name);
$funnel->published_at = null;
$funnel->save();
foreach ($templateFunnel->steps as $step) {
$step->duplicateTo($funnel);
}
foreach ($templateFunnel->productLinks as $link) {
$newLink = $link->replicate();
$newLink->funnel_id = $funnel->id;
$newLink->save();
}
return $funnel;
}
// -------------------------------------------------------------------------
// Cleanup
// -------------------------------------------------------------------------
public function deleteAndCleanup()
{
foreach ($this->steps as $step) {
$step->deleteAndCleanup();
}
$this->productLinks()->delete();
$this->domains()->delete();
$this->delete();
}
}