File: /home/xedaptot/be.naniguide.com/app/Model/FunnelStep.php
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use App\Library\Traits\HasUid;
use App\Library\Contracts\TemplateSubjectInterface;
use App\Library\GeneralTagResolver;
use App\Library\HtmlHandler\TransformTag;
use App\Services\TemplateService;
use League\Pipeline\PipelineBuilder;
use Validator;
/**
* @property \App\Model\Funnel|null $funnel
* @property \App\Model\Template|null $template
*/
class FunnelStep extends Model implements TemplateSubjectInterface
{
use HasUid;
protected $fillable = [
'name', 'type', 'slug', 'sort_order', 'settings', 'is_published',
];
protected $casts = [
'settings' => 'array',
'is_published' => 'boolean',
'sort_order' => 'integer',
];
// Step types
public const TYPE_LANDING = 'landing';
public const TYPE_OPTIN = 'optin';
public const TYPE_THANK_YOU = 'thank_you';
public const TYPE_SALES = 'sales';
public const TYPE_PRODUCT_DETAIL = 'product_detail';
public const TYPE_CART = 'cart';
public const TYPE_CHECKOUT = 'checkout';
public const TYPE_UPSELL = 'upsell';
public const TYPE_DOWNSELL = 'downsell';
public const TYPE_CUSTOM = 'custom';
public static function stepTypes()
{
return [
self::TYPE_LANDING,
self::TYPE_OPTIN,
self::TYPE_THANK_YOU,
self::TYPE_SALES,
self::TYPE_PRODUCT_DETAIL,
self::TYPE_CART,
self::TYPE_CHECKOUT,
self::TYPE_UPSELL,
self::TYPE_DOWNSELL,
self::TYPE_CUSTOM,
];
}
// -------------------------------------------------------------------------
// Boot
// -------------------------------------------------------------------------
protected static function boot()
{
parent::boot();
static::creating(function ($step) {
if (empty($step->slug) && $step->name) {
$step->slug = $step->generateUniqueSlug($step->name);
}
});
}
// -------------------------------------------------------------------------
// Relationships
// -------------------------------------------------------------------------
public function funnel()
{
return $this->belongsTo(Funnel::class);
}
public function template()
{
return $this->belongsTo(Template::class);
}
public function hasTemplate(): bool
{
return !is_null($this->template_id) && $this->template()->exists();
}
public function getThumbUrl(): string
{
return $this->template
? $this->template->getThumbUrl()
: url('images/placeholder.jpg');
}
public function setTemplate($template, string $name): void
{
TemplateService::for($this)->setTemplate($template, $name);
}
public function removeTemplate(): void
{
TemplateService::for($this)->removeTemplate();
}
public function setTemplateContent($content, $json): void
{
$this->template->updateContent($json, $content);
}
public function getTemplateContent(): string
{
return $this->template->content;
}
// -------------------------------------------------------------------------
// Scopes
// -------------------------------------------------------------------------
public function scopeSearch($query, $keyword)
{
if ($keyword) {
$query->where('name', 'like', '%' . trim($keyword) . '%');
}
}
public function scopePublished($query)
{
return $query->where('is_published', true);
}
public function scopeOrdered($query)
{
return $query->orderBy('sort_order');
}
// -------------------------------------------------------------------------
// CRUD helpers
// -------------------------------------------------------------------------
public function fillParams($params)
{
$this->name = $params['name'] ?? $this->name;
$this->type = $params['type'] ?? $this->type;
if (!empty($params['slug'])) {
$this->slug = $params['slug'];
}
if (isset($params['is_published'])) {
$this->is_published = (bool) $params['is_published'];
}
if (isset($params['settings'])) {
$this->settings = $params['settings'];
}
}
public function saveFromParams($params)
{
$this->fillParams($params);
$validator = Validator::make($params, [
'name' => 'required|max:255',
'type' => 'required|in:' . implode(',', self::stepTypes()),
]);
if ($validator->fails()) {
return $validator;
}
$this->save();
return $validator;
}
// -------------------------------------------------------------------------
// Slug
// -------------------------------------------------------------------------
public function generateUniqueSlug($name)
{
$slug = Str::slug($name);
$original = $slug;
$count = 1;
while (self::where('funnel_id', $this->funnel_id)
->where('slug', $slug)
->where('id', '!=', $this->id ?? 0)
->exists()
) {
$slug = $original . '-' . $count++;
}
return $slug;
}
// -------------------------------------------------------------------------
// URL
// -------------------------------------------------------------------------
public function getPublicUrl()
{
return url('f/' . $this->funnel->slug . '/' . $this->slug);
}
// -------------------------------------------------------------------------
// Content rendering
// -------------------------------------------------------------------------
public function renderedContent($content = null)
{
if (!$content && $this->template) {
$content = $this->template->content;
}
if (!$content) {
return '';
}
$resolvers = array_values(array_filter([
new GeneralTagResolver(),
$this->funnel?->mailList,
]));
return (new PipelineBuilder())
->add(new TransformTag(...$resolvers))
->build()
->process($content);
}
public function isStageExcluded(string $name): bool
{
return false;
}
// -------------------------------------------------------------------------
// Duplicate
// -------------------------------------------------------------------------
public function duplicateTo(Funnel $targetFunnel)
{
$copy = $this->replicate(['uid', 'slug']);
$copy->funnel_id = $targetFunnel->id;
$copy->slug = $copy->generateUniqueSlug($this->name);
$copy->template_id = null;
$copy->save();
if ($this->template) {
TemplateService::for($copy)->setTemplate($this->template, $copy->name);
}
return $copy;
}
public function deleteAndCleanup()
{
TemplateService::for($this)->removeTemplate();
$this->delete();
}
}