Files
Additional/app/Services/TrelloService.php
T
twotalesanimation d29f08bd98 fix: Extract design_name and print_size from order items for Trello
- design_name: Get product name from first order item
- print_size: Calculate from item dimensions (wallpaper: length in m, mural: width x height in cm)
- Add fallback values ('') when items/dimensions unavailable
- Prevents null errors when populating Trello custom fields
2026-01-02 16:11:15 +02:00

234 lines
6.6 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use DateTimeInterface;
class TrelloService
{
protected string $apiKey;
protected string $apiToken;
protected string $boardId;
protected string $baseUrl = 'https://api.trello.com/1';
public function __construct()
{
$this->apiKey = config('trello.api_key');
$this->apiToken = config('trello.api_token');
$this->boardId = config('trello.board_id');
}
/* =====================================================
| Public API
===================================================== */
/**
* Create a new Trello card and populate base fields
*/
public function createCard(
string $orderId,
string $orderNumber,
string $orderType = 'standard',
?string $startingListId = null
): ?string {
if (! $this->isConfigured()) {
Log::warning('Trello not configured, skipping card creation');
return null;
}
try {
$listId = $startingListId ?? $this->getStartingListId($orderType);
$response = Http::post("{$this->baseUrl}/cards", [
'name' => "Order #{$orderNumber}",
'desc' => "Order ID: {$orderId}\nType: {$orderType}",
'idList' => $listId,
'key' => $this->apiKey,
'token' => $this->apiToken,
]);
if (! $response->successful()) {
Log::error('Failed to create Trello card', [
'response' => $response->body(),
]);
return null;
}
$cardId = $response->json('id');
if ($cardId) {
$this->populateInitialFields($cardId, $orderId);
}
return $cardId;
} catch (\Throwable $e) {
Log::error('Exception creating Trello card', [
'error' => $e->getMessage(),
]);
return null;
}
}
/**
* Move card to another list
*/
public function moveCard(string $cardId, string $listName): bool
{
if (! $this->isConfigured()) {
return false;
}
$listId = $this->getListIdByName($listName);
if (! $listId) {
Log::warning('Trello list not found', ['list' => $listName]);
return false;
}
$response = Http::put("{$this->baseUrl}/cards/{$cardId}", [
'idList' => $listId,
'key' => $this->apiKey,
'token' => $this->apiToken,
]);
return $response->successful();
}
/**
* Attach a URL/file to a card
*/
public function attachFile(string $cardId, string $name, string $url): bool
{
$response = Http::post("{$this->baseUrl}/cards/{$cardId}/attachments", [
'name' => $name,
'url' => $url,
'key' => $this->apiKey,
'token' => $this->apiToken,
]);
return $response->successful();
}
/**
* Mark checklist item complete
*/
public function checkItem(string $cardId, string $checklistName, string $itemName): bool
{
$card = Http::get("{$this->baseUrl}/cards/{$cardId}", [
'checklists' => 'open',
'key' => $this->apiKey,
'token' => $this->apiToken,
])->json();
$checklist = collect($card['checklists'] ?? [])
->firstWhere('name', $checklistName);
if (! $checklist) {
return false;
}
$item = collect($checklist['checkItems'] ?? [])
->firstWhere('name', $itemName);
if (! $item) {
return false;
}
$response = Http::put(
"{$this->baseUrl}/checklists/{$checklist['id']}/checkItems/{$item['id']}",
[
'state' => 'complete',
'key' => $this->apiKey,
'token' => $this->apiToken,
]
);
return $response->successful();
}
/* =====================================================
| Custom Fields
===================================================== */
public function setTextField(string $cardId, string $fieldKey, string $value): bool
{
return $this->setCustomField($cardId, $fieldKey, ['text' => $value]);
}
public function setNumberField(string $cardId, string $fieldKey, float|int $value): bool
{
return $this->setCustomField($cardId, $fieldKey, ['number' => (string) $value]);
}
public function setDateField(string $cardId, string $fieldKey, DateTimeInterface $date): bool
{
return $this->setCustomField($cardId, $fieldKey, [
'date' => $date->format(DATE_ATOM),
]);
}
protected function setCustomField(string $cardId, string $fieldKey, array $value): bool
{
$fieldId = config("trello.custom_fields.{$fieldKey}");
if (! $fieldId) {
Log::warning('Trello custom field not configured', ['field' => $fieldKey]);
return false;
}
$response = Http::put(
"{$this->baseUrl}/cards/{$cardId}/customField/{$fieldId}/item",
[
'value' => $value,
'key' => $this->apiKey,
'token' => $this->apiToken,
]
);
return $response->successful();
}
/* =====================================================
| Internals
===================================================== */
protected function populateInitialFields(string $cardId, string $orderId): void
{
$this->setTextField($cardId, 'order_id', $orderId);
$this->setDateField($cardId, 'date_created', now());
}
protected function getStartingListId(string $orderType): string
{
return $orderType === 'custom'
? config('trello.lists.custom.new_custom_order')
: config('trello.lists.standard.new_order');
}
protected function getListIdByName(string $listName): ?string
{
$lists = array_merge(
config('trello.lists.standard', []),
config('trello.lists.custom', [])
);
foreach ($lists as $key => $id) {
if (strtolower(str_replace('_', ' ', $key)) === strtolower($listName)) {
return $id;
}
}
return null;
}
protected function isConfigured(): bool
{
return ! empty($this->apiKey)
&& ! empty($this->apiToken)
&& ! empty($this->boardId);
}
}