add: full multi-tenancy control

This commit is contained in:
Cauê Faleiros
2026-02-02 15:31:15 -03:00
commit c6ec92802b
1711 changed files with 258106 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
<?php
namespace Webkul\Marketing\Console\Commands;
use Illuminate\Console\Command;
use Webkul\Marketing\Helpers\Campaign;
class CampaignCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'campaign:process';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Process campaigns and send emails to the contact persons.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(protected Campaign $campaignHelper)
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🚀 Starting campaign processing...');
try {
$this->campaignHelper->process();
$this->info('✅ Campaign processing completed successfully!');
} catch (\Exception $e) {
$this->error('❌ An error occurred during campaign processing: '.$e->getMessage());
}
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Marketing\Contracts;
interface Campaign {}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Marketing\Contracts;
interface Event {}

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('marketing_events', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('description');
$table->date('date');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('marketing_events');
}
};

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('marketing_campaigns', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('subject');
$table->boolean('status')->default(0);
$table->string('type');
$table->string('mail_to');
$table->string('spooling')->nullable();
$table->unsignedInteger('marketing_template_id')->nullable();
$table->unsignedInteger('marketing_event_id')->nullable();
$table->timestamps();
$table->foreign('marketing_template_id')->references('id')->on('email_templates')->onDelete('set null');
$table->foreign('marketing_event_id')->references('id')->on('marketing_events')->onDelete('set null');
});
}
public function down(): void
{
Schema::dropIfExists('marketing_campaigns');
}
};

View File

@@ -0,0 +1,56 @@
<?php
namespace Webkul\Marketing\Helpers;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\Marketing\Mail\CampaignMail;
use Webkul\Marketing\Repositories\CampaignRepository;
use Webkul\Marketing\Repositories\EventRepository;
class Campaign
{
/**
* Create a new helper instance.
*
*
* @return void
*/
public function __construct(
protected EventRepository $eventRepository,
protected CampaignRepository $campaignRepository,
protected PersonRepository $personRepository,
) {}
/**
* Process the email.
*/
public function process(): void
{
$campaigns = $this->campaignRepository->getModel()
->leftJoin('marketing_events', 'marketing_campaigns.marketing_event_id', 'marketing_events.id')
->leftJoin('email_templates', 'marketing_campaigns.marketing_template_id', 'email_templates.id')
->select('marketing_campaigns.*')
->where('marketing_campaigns.status', 1)
->where(function ($query) {
$query->where('marketing_events.date', Carbon::now()->format('Y-m-d'))
->orWhereNull('marketing_events.date');
})
->get();
collect($campaigns)->each(function ($campaign) {
collect($this->getPersonsEmails())->each(fn ($email) => Mail::queue(new CampaignMail($email, $campaign)));
});
}
/**
* Get the email address.
*/
private function getPersonsEmails(): array
{
return $this->personRepository->pluck('emails')
->flatMap(fn ($emails) => collect($emails)->pluck('value'))
->all();
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Webkul\Marketing\Mail;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Webkul\Marketing\Contracts\Campaign;
class CampaignMail extends Mailable
{
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(
public string $email,
public Campaign $campaign
) {}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
to: [
new Address($this->email),
],
subject: $this->campaign->subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
htmlString: $this->campaign->email_template->content,
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Webkul\Marketing\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\EmailTemplate\Models\EmailTemplateProxy;
use Webkul\Marketing\Contracts\Campaign as CampaignContract;
class Campaign extends Model implements CampaignContract
{
/**
* Define the table for the model.
*
* @var string
*/
protected $table = 'marketing_campaigns';
/**
* The attributes that are fillable.
*
* @var array
*/
protected $fillable = [
'name',
'subject',
'status',
'marketing_template_id',
'marketing_event_id',
'spooling',
];
/**
* Get the email template
*/
public function email_template()
{
return $this->belongsTo(EmailTemplateProxy::modelClass(), 'marketing_template_id');
}
/**
* Get the event
*/
public function event()
{
return $this->belongsTo(EventProxy::modelClass(), 'marketing_event_id');
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Webkul\Marketing\Models;
use Konekt\Concord\Proxies\ModelProxy;
class CampaignProxy extends ModelProxy {}

View File

@@ -0,0 +1,32 @@
<?php
namespace Webkul\Marketing\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Marketing\Contracts\Event as EventContract;
class Event extends Model implements EventContract
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'marketing_events';
/**
* The attributes that are fillable.
*
* @var array
*/
protected $fillable = [
'name',
'description',
'date',
];
public function campaigns()
{
return $this->hasMany(CampaignProxy::modelClass(), 'marketing_event_id');
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Webkul\Marketing\Models;
use Konekt\Concord\Proxies\ModelProxy;
class EventProxy extends ModelProxy {}

View File

@@ -0,0 +1,44 @@
<?php
namespace Webkul\Marketing\Providers;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\ServiceProvider;
use Webkul\Marketing\Console\Commands\CampaignCommand;
class MarketingServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*/
public function boot(): void
{
$this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
$schedule->command('campaign:process')->daily();
});
}
/**
* Register services.
*/
public function register(): void
{
$this->registerCommands();
$this->app->register(ModuleServiceProvider::class);
}
/**
* Register the commands.
*/
private function registerCommands(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
CampaignCommand::class,
]);
}
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Webkul\Marketing\Providers;
use Webkul\Core\Providers\BaseModuleServiceProvider;
class ModuleServiceProvider extends BaseModuleServiceProvider
{
/**
* Define the module's array.
*
* @var array
*/
protected $models = [
\Webkul\Marketing\Models\Event::class,
\Webkul\Marketing\Models\Campaign::class,
];
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Webkul\Marketing\Repositories;
use Webkul\Core\Eloquent\Repository;
use Webkul\Marketing\Contracts\Campaign;
class CampaignRepository extends Repository
{
/**
* Specify Model class name.
*/
public function model(): string
{
return Campaign::class;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Webkul\Marketing\Repositories;
use Webkul\Core\Eloquent\Repository;
use Webkul\Marketing\Contracts\Event;
class EventRepository extends Repository
{
/**
* Specify Model class name.
*/
public function model(): string
{
return Event::class;
}
}