Czy kiedykolwiek zastanawiales sie jak firmy sledza tysiace przesylek w czasie rzeczywistym? Albo jak platformy tradingowe aktualizuja ceny natychmiast na tysiacach ekranow? Budowalem te systemy w skali, przetwarzajac ponad 2 miliardy dolarow rocznych transakcji. Dzisiaj pokaze ci dokladnie jak zbudowac gotowy do produkcji dashboard w czasie rzeczywistym uzywajac Laravel, WebSockets i Vue.js.
To nie jest teoria - to sprawdzone w boju wzory z rzeczywistych systemow produkcyjnych obslugujacych operacje logistyczne 24/7.
Co budujemy
Stworzymy dashboard logistyczny w czasie rzeczywistym ktory:
- Sledzi lokalizacje przesylek z aktualizacjami na zywo
- Pokazuje metryki w czasie rzeczywistym (dostawy, przychod, wydajnosc)
- Implementuje system blokad zeby zapobiec konfliktowym edycjom
- Obsluguje tysiace jednoczesnych polaczen
- Skaluje sie horyzontalnie z Redis pub/sub
Oto jak wyglada finalny dashboard:
- Wielu uzytkownikow widzi aktualizacje natychmiast
- Statusy przesylek zmieniaja sie w czasie rzeczywistym
- Metryki aktualizuja sie bez odswiezania strony
- Blokady edycji zapobiegaja konfliktom danych
Wymagania wstepne
Bedziesz potrzebowal:
- PHP 8.1+ z Laravel 10+
- Node.js 18+ dla Vue 3
- Redis (lub KeyDB dla lepszej wydajnosci)
- Podstawowa znajomosc Laravel i Vue
- Zainstalowane Composer i npm
Czesc 1: Konfiguracja backendu Laravel
Krok 1: Utworz projekt Laravel
composer create-project laravel/laravel realtime-logistics
cd realtime-logistics
# Zainstaluj zaleznosci broadcasting i WebSocket
composer require pusher/pusher-php-server
composer require predis/predis
# Zainstaluj pakiet Laravel WebSockets (samodzielna alternatywa dla Pusher)
composer require beyondcode/laravel-websocketsKrok 2: Skonfiguruj Broadcasting
Zaktualizuj plik .env:
BROADCAST_DRIVER=pusher
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
# Uzyj Laravel WebSockets jako zamiennika Pusher
PUSHER_APP_ID=local-app
PUSHER_APP_KEY=local-key
PUSHER_APP_SECRET=local-secret
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
# Konfiguracja Redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379Krok 3: Schemat bazy danych
Utworz migracje dla naszego systemu logistycznego:
php artisan make:migration create_shipments_table
php artisan make:migration create_metrics_table
php artisan make:migration create_edit_locks_table// database/migrations/create_shipments_table.php
Schema::create('shipments', function (Blueprint $table) {
$table->id();
$table->string('tracking_number')->unique();
$table->string('origin');
$table->string('destination');
$table->enum('status', ['pending', 'in_transit', 'delivered', 'exception']);
$table->decimal('latitude', 10, 7)->nullable();
$table->decimal('longitude', 10, 7)->nullable();
$table->decimal('value', 10, 2);
$table->json('metadata')->nullable();
$table->timestamps();
$table->index(['status', 'created_at']); // Optymalizacja dla zapytan dashboardu
$table->index(['latitude', 'longitude']); // Zapytania przestrzenne
});
// database/migrations/create_metrics_table.php
Schema::create('metrics', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->decimal('value', 15, 2);
$table->string('unit')->nullable();
$table->timestamp('calculated_at');
$table->timestamps();
$table->index('calculated_at');
});
// database/migrations/create_edit_locks_table.php
Schema::create('edit_locks', function (Blueprint $table) {
$table->id();
$table->morphs('lockable'); // Polimorficzne - moze blokowac dowolny model
$table->unsignedBigInteger('user_id');
$table->timestamp('locked_at');
$table->timestamp('expires_at');
$table->index(['lockable_type', 'lockable_id', 'expires_at']);
});Krok 4: Utworz modele
// app/Models/Shipment.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use App\Traits\Lockable;
use App\Events\ShipmentUpdated;
class Shipment extends Model
{
use HasFactory, Lockable;
protected $fillable = [
'tracking_number', 'origin', 'destination',
'status', 'latitude', 'longitude', 'value', 'metadata'
];
protected $casts = [
'metadata' => 'array',
'value' => 'decimal:2',
'latitude' => 'decimal:7',
'longitude' => 'decimal:7',
];
protected $dispatchesEvents = [
'updated' => ShipmentUpdated::class,
];
// Optymalizacja swiata rzeczywistego: Cache'uj czesto odwiedzane przesylki
public static function findByTrackingCached($trackingNumber)
{
return Cache::remember(
"shipment.{$trackingNumber}",
now()->addMinutes(5),
fn() => self::where('tracking_number', $trackingNumber)->first()
);
}
// Zapytanie geoprzestrzenne dla poblizkich przesylek
public function scopeNearby($query, $lat, $lng, $radiusKm = 50)
{
// Wzor Haversine do obliczania odleglosci
$haversine = "(
6371 * acos(
cos(radians(?)) * cos(radians(latitude)) *
cos(radians(longitude) - radians(?)) +
sin(radians(?)) * sin(radians(latitude))
)
)";
return $query
->selectRaw("*, {$haversine} AS distance", [$lat, $lng, $lat])
->having('distance', '<=', $radiusKm)
->orderBy('distance');
}
}Krok 5: Zaimplementuj trait Lockable
To zapobiega konfliktowym edycjom - kluczowe dla danych finansowych:
// app/Traits/Lockable.php
namespace App\Traits;
use App\Models\EditLock;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
trait Lockable
{
public function locks()
{
return $this->morphMany(EditLock::class, 'lockable');
}
public function acquireLock($userId = null, $duration = 300)
{
$userId = $userId ?? Auth::id();
// Sprawdz istniejace wazne blokady
$existingLock = $this->locks()
->where('expires_at', '>', now())
->where('user_id', '!=', $userId)
->first();
if ($existingLock) {
return false; // Ktos inny ma blokade
}
// Wyczysc wygasle blokady
$this->locks()->where('expires_at', '<=', now())->delete();
// Utworz nowa blokade
return $this->locks()->create([
'user_id' => $userId,
'locked_at' => now(),
'expires_at' => now()->addSeconds($duration),
]);
}
public function releaseLock($userId = null)
{
$userId = $userId ?? Auth::id();
return $this->locks()
->where('user_id', $userId)
->delete();
}
public function isLocked($excludeUserId = null)
{
$query = $this->locks()->where('expires_at', '>', now());
if ($excludeUserId) {
$query->where('user_id', '!=', $excludeUserId);
}
return $query->exists();
}
public function currentLock()
{
return $this->locks()
->where('expires_at', '>', now())
->with('user')
->first();
}
}Krok 6: Utworz eventy broadcastingu
// app/Events/ShipmentUpdated.php
namespace App\Events;
use App\Models\Shipment;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ShipmentUpdated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $shipment;
public function __construct(Shipment $shipment)
{
$this->shipment = $shipment;
}
public function broadcastOn()
{
return [
new Channel('shipments'),
new Channel("shipment.{$this->shipment->id}"),
];
}
public function broadcastWith()
{
return [
'id' => $this->shipment->id,
'tracking_number' => $this->shipment->tracking_number,
'status' => $this->shipment->status,
'latitude' => $this->shipment->latitude,
'longitude' => $this->shipment->longitude,
'value' => $this->shipment->value,
'updated_at' => $this->shipment->updated_at->toIso8601String(),
];
}
// Optymalizacja wydajnosci: Uzyj kolejki do broadcastingu
public function broadcastQueue()
{
return 'broadcasts';
}
}Ten artykul jest obszerny i zawiera pelna implementacje dashboardu w czasie rzeczywistym. Pelna wersja w jezyku angielskim zawiera:
- Kompletna implementacje Vue.js frontendu z aktualizacjami w czasie rzeczywistym
- Konfiguracje Pinia stores
- Optymalizacje Redis i skalowanie
- Konfiguracje kolejek dla wysokiej przepustowosci
- Wdrozenie produkcyjne z Supervisor
- Optymalizacje Redis
- Konfiguracje Nginx dla WebSockets
- Wskazowki i spostrzezenia produkcyjne
- Strategie skalowania
- Monitorowanie i debugowanie
- Testowanie systemu
Podsumowanie
Ten wzor dashboardu w czasie rzeczywistym sprawdzil sie w produkcji, obslugujac miliony aktualizacji dziennie. Kombinacja eleganckiego backendu Laravel, reaktywnego frontendu Vue i blyskawiocznego Redis pub/sub tworzy system ktory jest zarowno wydajny jak i latwy w utrzymaniu.
Kluczowe wnioski:
- Projektuj pod skale od pierwszego dnia - Trudniej refaktoryzowac pozniej
- Oddziel odpowiedzialnosci - Broadcasty, kolejki i wywolania API powinny uzywac roznych kanalow
- Monitoruj wszystko - Nie mozesz optymalizowac czegos czego nie mierzysz
- Testuj z realistycznymi danymi - 10 rekordow testowych nie ujawni problemow wydajnosciowych
Wzory pokazane tutaj nie sa tylko teoretyczne - dzialaja w produkcji wlasnie teraz, sledzac prawdziwe przesylki warte prawdziwe pieniadze.
Opanuj najpierw podstawy Laravel
Budowanie dashboardow w czasie rzeczywistym wymaga solidnej znajomosci Laravel. Jesli jestes nowy w Laravel, zacznij od tych kompleksowych samouczkow:
- Zbuduj bloga z Laravel - Naucz sie Eloquent, uwierzytelniania i operacji CRUD
- Zbuduj portfolio z Laravel - Opanuj przesylanie plikow, relacje i panele administracyjne
- Zbuduj e-commerce z Laravel - Zaawansowane wzory wlaczajac kolejki i obsluge zdarzen
Kazdy samouczek zawiera prompty wspomagane przez AI ktore przeprowadza cie przez budowanie gotowych do produkcji aplikacji od zera.
Masz pytania o implementacje tego w twoim stacku? Zostaw komentarz - prawdopodobnie stalem przed (i rozwiazalem) ten sam problem w produkcji.
Fred
AUTHORFull-stack developer with 10+ years building production applications. I write about cloud deployment, DevOps, and modern web development from real-world experience.
Need a developer who gets it?
POC builds, vibe-coded fixes, and real engineering. Let's talk.
Hire Me →
