engeni/api-client

Engeni - API client


README

A PHP library providing Laravel Eloquent-style interface for interacting with Engeni RESTful APIs. This client is designed to bring the familiar syntax and patterns of Laravel's Eloquent ORM to RESTful API consumption.

Overview

The Engeni API Client allows developers to query and manipulate API resources using an Eloquent-like syntax, providing a fluid, intuitive interface for working with Engeni's RESTful API endpoints. Instead of crafting raw HTTP requests, you can use the same elegant query builder pattern that Laravel developers are familiar with.

Installation

composer require engeni/api-client

Configuration

Basic Client Setup

use Engeni\ApiClient\Client;
use Engeni\ApiClient\Query;

// Initialize client with your API credentials
$client = new Client([
    'base_uri' => 'https://api.test',        // Your API base URL
    'debug' => true,                         // Enable/disable debug mode
    'auth_token' => 'YOUR_BEARER_TOKEN'      // Authorization token
]);

// Set as global client instance for use throughout your application
$client->setAsGlobal();

// Make a basic request to test connectivity
$query = (new Query)->setPath('/');
$response = $client->get($query);

// Decode and inspect the response
$apiStatus = json_decode($response->getBody(), true);
echo "API Status: " . $apiStatus['status'];

Production Configuration

// Production-ready configuration
$client = new Client([
    'base_uri' => 'https://api.engeni.com',  // Production API URL
    'debug' => false,                        // Disable debug in production
    'auth_token' => getenv('ENGENI_API_TOKEN'), // Use environment variable
    'timeout' => 30,                         // Request timeout in seconds
    'headers' => [
        'User-Agent' => 'MyApp/1.0',
        'X-Client-Version' => '1.0.0'
    ]
]);

$client->setAsGlobal();

Multi-Environment Configuration

// Environment-based configuration
$environments = [
    'local' => [
        'base_uri' => 'https://api.test',
        'debug' => true,
        'timeout' => 60
    ],
    'staging' => [
        'base_uri' => 'https://api-staging.engeni.com',
        'debug' => false,
        'timeout' => 30
    ],
    'production' => [
        'base_uri' => 'https://api.engeni.com',
        'debug' => false,
        'timeout' => 30
    ]
];

$currentEnv = getenv('APP_ENV') ?: 'local';
$config = $environments[$currentEnv];

$client = new Client(array_merge($config, [
    'auth_token' => getenv('ENGENI_API_TOKEN')
]));

$client->setAsGlobal();

Service-Specific Client Configuration

use Engeni\ApiClient\Client;
use Engeni\ApiClient\Resource\CXM\User;
use Engeni\ApiClient\Resource\Toby\Account;

// Configure for CXM service
$cxmClient = new Client([
    'base_uri' => 'https://cxm-api.engeni.com',
    'debug' => true,
    'auth_token' => $cxmToken,
    'headers' => [
        'X-Service' => 'cxm'
    ]
]);

// Configure for Toby service
$tobyClient = new Client([
    'base_uri' => 'https://toby-api.engeni.com',
    'debug' => true,
    'auth_token' => $tobyToken,
    'headers' => [
        'X-Service' => 'toby'
    ]
]);

// Use specific client for different services
User::setClient($cxmClient);      // CXM resources use CXM client
Account::setClient($tobyClient);  // Toby resources use Toby client

Application Context Configuration

use Engeni\ApiClient\Client;

// Set application context for multi-tenancy
$client = new Client([
    'base_uri' => 'https://api.engeni.com',
    'debug' => true,
    'auth_token' => $token,
    'headers' => [
        Client::APP_CONTEXT_ACCOUNT_HEADER => '123',   // Current account ID
        Client::APP_CONTEXT_LOCATION_HEADER => '456'   // Current location ID
    ]
]);

$client->setAsGlobal();

// All subsequent requests will include the account and location context
$users = User::where('account_id', 123)->get(); // Automatically scoped to account 123

Multi-Service Architecture

The Engeni API Client is designed to work with multiple microservices, each handling specific business domains. This architecture allows for better scalability, maintainability, and service isolation.

Service Organization

Each service has its own namespace and resource classes:

Engeni\ApiClient\Resource\
├── CXM\              # Customer Experience Management
│   ├── User.php
│   ├── Account.php
│   ├── Campaign.php
│   ├── Order.php
│   └── ...
├── Toby\             # Billing & Invoicing
│   ├── Account.php
│   ├── Invoice.php
│   ├── PaymentMethod.php
│   └── ...
├── LaGuiaOnline\    # Business Directory
│   ├── Business.php
│   ├── Category.php
│   ├── City.php
│   └── ...
├── Flokee\          # E-commerce
│   ├── Product.php
│   ├── Kpi.php
│   └── ...
├── Landkit\         # Landing Pages & Marketing
│   ├── Landing.php
│   ├── Contact.php
│   └── ...
└── MediaManager\    # Digital Asset Management
    ├── Customer.php
    └── MarketingPlan.php

Service-Specific Base Classes

Each service extends a common base class that handles service-specific configuration:

// CXM resources inherit from CXM\BaseResource
class User extends \Engeni\ApiClient\Resource\CXM\BaseResource
{
    protected $resourceName = 'users';
    protected $rootPath = 'cxm'; // API path prefix
}

// Toby resources inherit from Toby\BaseResource
class Invoice extends \Engeni\ApiClient\Resource\Toby\BaseResource
{
    protected $resourceName = 'invoices';
    protected $rootPath = 'toby'; // API path prefix
}

Cross-Service Communication

You can work with multiple services in the same application:

use Engeni\ApiClient\Resource\CXM\User;
use Engeni\ApiClient\Resource\Toby\Invoice;
use Engeni\ApiClient\Resource\LaGuiaOnline\Business;

// Get user from CXM service
$user = User::find($userId);

// Get user's invoices from Toby service
$invoices = Invoice::fromAccount($user->account_id)->get();

// Get user's businesses from LaGuiaOnline service
$businesses = Business::where('account_id', $user->account_id)->get();

Service-Specific Features

Each service may have unique features and methods:

CXM Service:

  • User role management (isAdmin(), isAnalyst(), isSales())
  • Campaign management with AdWords integration
  • Advanced user account switching

Toby Service:

  • Invoice processing and payment handling
  • Account setup and billing configuration
  • Transaction management

LaGuiaOnline Service:

  • Business category management (addCategory(), removeCategory())
  • Geographic location handling
  • Business publishing workflow

Flokee Service:

  • E-commerce KPI tracking
  • Product catalog management
  • Sales analytics

Eloquent-Style API Querying

The client follows Laravel's Eloquent ORM patterns to provide a familiar experience:

// Find a resource by ID
$user = User::find($userId);

// Build complex queries with filtering
$activeAccounts = Account::select('id', 'name')
    ->where('active', '1')
    ->limit(2)
    ->get();

// Eager load relationships
$userWithProfile = User::with('profile')->find($userId);

RESTful Resource Operations

The client maps HTTP verbs to appropriate methods according to RESTful principles:

Retrieving Resources

// Get all resources (paginated by default)
$users = User::all();

// Find a specific resource
$user = User::find($userId);

// Get the first matching resource
$firstActiveAccount = Account::where('active', '1')->first();

// Use firstOrFail to throw an exception if no resource is found
try {
    $user = User::where('email', 'nonexistent@example.com')->firstOrFail();
} catch (ResourceNotFoundException $e) {
    // Handle the error
}

// Get an array of values from a specific column
$accountNames = Account::pluck('name', 'id'); // Returns ['id' => 'name', ...]

Creating Resources

// Create a new resource
$user = User::create([
    'email' => 'user@example.com',
    'first_name' => 'John',
    'last_name' => 'Doe'
]);

Updating Resources

// Update via fill and save
$user = User::find($userId);
$user->fill([
    'first_name' => 'Updated Name'
]);
$user->save();

// Or use the update method
$user->update([
    'first_name' => 'Updated Name'
]);

Deleting Resources

// Delete a resource
$user->delete();

// Or use the destroy method
User::destroy($userId);

// Delete multiple resources
User::destroy([$userId1, $userId2, $userId3]);

Query Builder Features

The query builder supports all the API conventions for filtering, sorting, and field selection:

Field Selection

// Select specific fields
$users = User::select('id', 'email', 'first_name')->get();

// Alternative using fields parameter
$users = User::get(['fields' => 'id,email,first_name']);

Filtering

// Simple filtering
$activeAccounts = Account::where('active', '1')->get();

// Multiple filters
$users = User::where('status', 'active')
    ->where('role', 'admin')
    ->get();

// Using whereIn for multiple possible values
$products = Product::whereIn('category', ['electronics', 'computers'])
    ->get();

Sorting

// Sort by a field (ascending)
$users = User::orderBy('created_at')->get();

// Sort by a field (descending)
$users = User::orderBy('created_at', 'DESC')->get();

// Multiple sorting criteria
$users = User::orderBy('last_name', 'ASC')
    ->orderBy('first_name', 'ASC')
    ->get();

// Alternative using sort parameter
$users = User::get(['sort' => '-created_at']);

Pagination

// Get paginated results
$users = User::paginate();

// Specify page size
$users = User::paginate(15);

// Specify the page and page size
$users = User::forPage(2, 15)->get();

// Access pagination information
echo "Showing page {$users->current_page} of {$users->last_page}";
echo "Total items: {$users->total}";

// Iterate through results
foreach ($users as $user) {
    // Process each user
}

// Access pagination metadata
$paginationInfo = [
    'current_page' => $users->current_page,
    'first_page_url' => $users->first_page_url,
    'from' => $users->from,
    'last_page' => $users->last_page,
    'last_page_url' => $users->last_page_url,
    'next_page_url' => $users->next_page_url,
    'per_page' => $users->per_page,
    'prev_page_url' => $users->prev_page_url,
    'to' => $users->to,
    'total' => $users->total
];

Relationships

// Eager load relationships
$user = User::with('profile')->find($userId);

// Multiple relationships
$user = User::with(['profile', 'accounts'])->find($userId);

// Nested relationships
$user = User::with('accounts.permissions')->find($userId);

// Alternative using embed parameter
$user = User::find($userId, ['embed' => 'profile,accounts']);

Available Resources

The client provides access to various Engeni API resources organized by service. Each service has its own namespace and specific resources:

CXM (Customer Experience Management) Resources

User Management

use Engeni\ApiClient\Resource\CXM\User;
use Engeni\ApiClient\Resource\CXM\Account;
use Engeni\ApiClient\Resource\CXM\Role;

// Find users with specific roles
$adminUsers = User::where('role_id', Role::ROLE_USER_ADMIN)->get();

// Check user roles
$user = User::find($userId);
if ($user->isAdmin()) {
    // User has admin privileges
}

// Set user account and password
$user->setCurrentAccount(['account_id' => 123]);
$user->setCurrentPassword([
    'current_password' => 'oldpass',
    'password' => 'newpass',
    'password_confirmation' => 'newpass'
]);

Campaign and Marketing Resources

use Engeni\ApiClient\Resource\CXM\Campaign;
use Engeni\ApiClient\Resource\CXM\Adwords;
use Engeni\ApiClient\Resource\CXM\Notification;

// Get active campaigns with performance data
$campaigns = Campaign::where('status', 'active')
    ->with('adwords', 'notifications')
    ->get();

// Create a new notification
$notification = Notification::create([
    'title' => 'Welcome Email',
    'content' => 'Welcome to our platform!',
    'campaign_id' => $campaignId,
    'scheduled_at' => '2024-01-01T10:00:00Z'
]);

Order and Billing Resources

use Engeni\ApiClient\Resource\CXM\Order;
use Engeni\ApiClient\Resource\CXM\Invoice;
use Engeni\ApiClient\Resource\CXM\Plan;

// Get orders with pagination
$orders = Order::with('plan', 'account')
    ->orderBy('created_at', 'DESC')
    ->paginate(20);

// Process invoice payment
$invoice = Invoice::find($invoiceId);
$result = $invoice->do('process-payment', [
    'verb' => 'POST',
    'form_params' => [
        'payment_method_id' => $paymentMethodId
    ]
]);

Toby (Billing & Invoicing) Resources

use Engeni\ApiClient\Resource\Toby\Account;
use Engeni\ApiClient\Resource\Toby\Invoice;
use Engeni\ApiClient\Resource\Toby\PaymentMethod;

// Setup account for billing
$result = Account::setup($accountId);

// Get account invoices
$invoices = Invoice::fromAccount($accountId)
    ->where('status', 'unpaid')
    ->get();

// Manage payment methods
$paymentMethods = PaymentMethod::fromAccount($accountId)->get();
$defaultMethod = PaymentMethod::fromAccount($accountId)
    ->where('default', true)
    ->first();

LaGuiaOnline (Business Directory) Resources

use Engeni\ApiClient\Resource\LaGuiaOnline\Business;
use Engeni\ApiClient\Resource\LaGuiaOnline\Category;
use Engeni\ApiClient\Resource\LaGuiaOnline\City;

// Find businesses by category and location
$businesses = Business::where('category_id', $categoryId)
    ->where('city_id', $cityId)
    ->with('categories', 'city', 'state')
    ->get();

// Manage business categories
$business = Business::find($businessId);
$business->addCategory($categoryId);
$business->removeCategory($categoryId);

// Publish business listing
$business->publish();

Flokee (E-commerce) Resources

use Engeni\ApiClient\Resource\Flokee\Ecommerce;
use Engeni\ApiClient\Resource\Flokee\Product;
use Engeni\ApiClient\Resource\Flokee\Kpi;

// Get e-commerce KPIs
$kpis = Kpi::fromAccount($accountId)
    ->where('date_from', '2024-01-01')
    ->where('date_to', '2024-12-31')
    ->get();

// Manage products
$products = Product::where('status', 'active')
    ->with('categories')
    ->paginate();

Other Service Resources

Landkit Resources

use Engeni\ApiClient\Resource\Landkit\Landing;
use Engeni\ApiClient\Resource\Landkit\Contact;

// Get landing pages with tracking
$landings = Landing::with('trackingCode', 'publishers')->get();

// Handle contact form submissions
$contacts = Contact::fromLanding($landingId)->get();

MediaManager Resources

use Engeni\ApiClient\Resource\MediaManager\Customer;
use Engeni\ApiClient\Resource\MediaManager\MarketingPlan;

// Get customer marketing plans
$plans = MarketingPlan::fromCustomer($customerId)->get();

Practical Use Cases

Working with Parent Resources

Many resources in the Engeni API have parent-child relationships. The client provides methods to work with these relationships:

// Get child resources from a parent
$childResources = ChildResource::fromParent($parentId)
    ->where('status', 'active')
    ->get();

// Toby: Get invoices from an account
$invoices = Invoice::fromAccount($accountId)
    ->where('status', 'unpaid')
    ->orderBy('due_date', 'ASC')
    ->get();

// Toby: Get payment methods from an account
$paymentMethods = PaymentMethod::fromAccount($accountId)
    ->where('default', true)
    ->get();

// LaGuiaOnline: Get businesses from a category
$businesses = Business::fromCategory($categoryId)
    ->where('active', true)
    ->with('city', 'state')
    ->paginate();

// Get specific attributes from parent-related resources
$cardTypes = PaymentMethod::fromAccount($accountId)
    ->where('default', true)
    ->pluck('type', 'id');

User Authentication and Role Management

use Engeni\ApiClient\Resource\CXM\User;
use Engeni\ApiClient\Resource\CXM\Role;

// Authenticate user and check permissions
$user = User::find($userId);

if ($user->isCxmUser()) {
    if ($user->isAdmin()) {
        // Grant full access
        $accounts = Account::all();
    } elseif ($user->isAnalyst()) {
        // Grant analytics access
        $campaigns = Campaign::where('account_id', $user->account_id)->get();
    } elseif ($user->isSales()) {
        // Grant sales access
        $orders = Order::where('account_id', $user->account_id)->get();
    }
}

// Update user account context
$user->setCurrentAccount(['account_id' => $newAccountId]);

// Change user password
$user->setCurrentPassword([
    'current_password' => 'currentPass123',
    'password' => 'newPass456',
    'password_confirmation' => 'newPass456'
]);

E-commerce Management

use Engeni\ApiClient\Resource\Flokee\Product;
use Engeni\ApiClient\Resource\Flokee\Kpi;

// Get product performance KPIs
$salesKpis = Kpi::fromAccount($accountId)
    ->where('date_from', '2024-01-01T00:00:00Z')
    ->where('date_to', '2024-12-31T23:59:59Z')
    ->where('kpi_type', 'sales')
    ->get();

// Manage product catalog
$activeProducts = Product::where('status', 'active')
    ->where('stock_quantity', '>', 0)
    ->with('categories', 'images')
    ->orderBy('name')
    ->paginate(50);

// Update product information
$product = Product::find($productId);
$product->fill([
    'name' => 'Updated Product Name',
    'price' => 29.99,
    'stock_quantity' => 100
]);
$product->save();

Business Directory Management

use Engeni\ApiClient\Resource\LaGuiaOnline\Business;
use Engeni\ApiClient\Resource\LaGuiaOnline\Category;

// Find businesses by multiple criteria
$businesses = Business::where('account_id', $accountId)
    ->where('active', true)
    ->with('categories', 'city', 'state', 'country')
    ->orderBy('name')
    ->get();

// Manage business categories dynamically
$business = Business::find($businessId);

// Add multiple categories
$business->addCategory($primaryCategoryId);
$business->addCategory($secondaryCategoryId);

// Remove a category
$business->removeCategory($oldCategoryId);

// Publish business to directory
$result = $business->publish();

Campaign and Marketing Automation

use Engeni\ApiClient\Resource\CXM\Campaign;
use Engeni\ApiClient\Resource\CXM\Notification;

// Get campaign performance with related data
$campaigns = Campaign::where('status', 'active')
    ->with('adwords', 'notifications', 'orders')
    ->orderBy('created_at', 'DESC')
    ->get();

// Create automated email notification
$notification = Notification::create([
    'campaign_id' => $campaignId,
    'title' => 'Welcome Series - Day 1',
    'content' => '<h1>Welcome to our platform!</h1><p>Thank you for joining...</p>',
    'scheduled_at' => '2024-01-01T09:00:00Z',
    'target_audience' => 'new_subscribers'
]);

// Send immediate notification
$notification->send();

Billing and Invoice Management

use Engeni\ApiClient\Resource\Toby\Invoice;
use Engeni\ApiClient\Resource\Toby\ChargeTransaction;

// Get overdue invoices
$overdueInvoices = Invoice::fromAccount($accountId)
    ->where('status', 'unpaid')
    ->where('due_date', '<', now()->toISOString())
    ->with('transactions')
    ->get();

// Process invoice payment
$invoice = Invoice::find($invoiceId);
$paymentResult = $invoice->do('process-payment', [
    'verb' => 'POST',
    'form_params' => [
        'payment_method_id' => $paymentMethodId,
        'amount' => $invoice->total_amount
    ]
]);

// Get transaction history
$transactions = ChargeTransaction::fromAccount($accountId)
    ->where('invoice_id', $invoiceId)
    ->orderBy('created_at', 'DESC')
    ->get();

Landing Page and Lead Management

use Engeni\ApiClient\Resource\Landkit\Landing;
use Engeni\ApiClient\Resource\Landkit\Contact;

// Get landing page performance
$landings = Landing::with('trackingCode', 'publishers')
    ->where('status', 'published')
    ->orderBy('conversion_rate', 'DESC')
    ->get();

// Handle contact form submissions
$contacts = Contact::fromLanding($landingId)
    ->where('created_at', '>', '2024-01-01T00:00:00Z')
    ->orderBy('created_at', 'DESC')
    ->get();

// Export contacts for CRM
$contactData = Contact::fromLanding($landingId)
    ->select('email', 'first_name', 'last_name', 'phone')
    ->where('subscribed', true)
    ->get()
    ->toArray();

Data Formats

Date-Time Format

The API follows RFC3339 standards for date-time values and always uses UTC timezone. When working with dates:

  • All date-time values must be in RFC3339 format: YYYY-MM-DDThh:mm:ss.sssZ
  • UTC timezone is represented by Z or +00:00 suffix
  • The client automatically handles date-time conversion between PHP and API formats

Valid date-time examples:

2020-01-01T10:00:00.00000Z
2020-01-01T10:00:00Z
2020-01-01T10:00:00+00:00
// When sending dates to the API
$resource->created_at = '2020-01-01T10:00:00Z';

// When receiving dates from the API
$date = new DateTime($resource->created_at);

JSON Format

All API responses follow a consistent JSON structure:

{
    "code": 200,
    "status": "success",
    "data": {
        // Resource data here
    }
}

Error Handling

The client maps API error responses to appropriate exceptions. The Engeni API follows a standardized error response structure:

{
  "status": "error",
  "error": {
    "code": 422,
    "message": "The given data was invalid.",
    "errors": {
      "password": [
        "The password field is required",
        "The password field must have at least 8 characters",
        "The password field must include one number"
      ]
    }
  }
}

In development environments, additional debug information is provided:

{
  "status": "error",
  "error": {
    "code": 405,
    "message": "Method PUT not allowed for this endpoint"
  },
  "debug": {
    "file": "/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php",
    "line": 232,
    "trace": "#0 [...] RoutesRequests.php(170) {main}"
  }
}

Exception Types

The client provides specific exception types for different error scenarios:

  • ResourceNotFoundException - Thrown when a resource is not found (404)
  • AuthorizationException - Thrown for authentication/authorization errors (401/403)
  • ApiResponseException - Thrown for general API errors with response details
  • ApiClientException - Thrown for client-side errors (network issues, timeouts, etc.)

Handling API Errors

use Engeni\ApiClient\Exceptions\ResourceNotFoundException;
use Engeni\ApiClient\Exceptions\ApiResponseException;
use Engeni\ApiClient\Exceptions\ApiClientException;

try {
    $user = User::findOrFail(999); // Non-existent user
} catch (ResourceNotFoundException $e) {
    // Handle 404 Not Found
    echo "User not found: " . $e->getMessage();
} catch (ApiResponseException $e) {
    // Handle API validation/other errors
    if ($e->getCode() === 422) {
        // Validation errors
        $errors = $e->getResponseData()['error']['errors'] ?? [];
        foreach ($errors as $field => $messages) {
            echo "$field: " . implode(', ', $messages) . "\n";
        }
    } else {
        echo "API Error ({$e->getCode()}): " . $e->getMessage();
    }
} catch (ApiClientException $e) {
    // Handle client-side errors (network, timeout, etc.)
    echo "Connection Error: " . $e->getMessage();
}

Service-Specific Error Handling

Different services may have specific error patterns:

CXM Service Error Handling

use Engeni\ApiClient\Resource\CXM\User;
use Engeni\ApiClient\Exceptions\ApiResponseException;

try {
    // Attempt to change password
    $user = User::find($userId);
    $user->setCurrentPassword([
        'current_password' => 'wrong',
        'password' => 'newpass123',
        'password_confirmation' => 'newpass123'
    ]);
} catch (ApiResponseException $e) {
    if ($e->getCode() === 422) {
        // Handle password validation errors
        $responseData = $e->getResponseData();
        $errors = $responseData['error']['errors'] ?? [];

        if (isset($errors['current_password'])) {
            echo "Current password is incorrect\n";
        }
        if (isset($errors['password'])) {
            echo "New password: " . implode(', ', $errors['password']) . "\n";
        }
    }
}

Toby Service Error Handling

use Engeni\ApiClient\Resource\Toby\Invoice;
use Engeni\ApiClient\Exceptions\ApiResponseException;

try {
    // Attempt to process payment
    $invoice = Invoice::find($invoiceId);
    $result = $invoice->do('process-payment', [
        'verb' => 'POST',
        'form_params' => [
            'payment_method_id' => $invalidPaymentMethodId
        ]
    ]);
} catch (ApiResponseException $e) {
    if ($e->getCode() === 400) {
        // Handle payment processing errors
        echo "Payment failed: " . $e->getMessage();
    } elseif ($e->getCode() === 422) {
        // Handle validation errors
        $responseData = $e->getResponseData();
        $errors = $responseData['error']['errors'] ?? [];
        foreach ($errors as $field => $messages) {
            echo "Payment $field: " . implode(', ', $messages) . "\n";
        }
    }
}

LaGuiaOnline Service Error Handling

use Engeni\ApiClient\Resource\LaGuiaOnline\Business;
use Engeni\ApiClient\Exceptions\ApiResponseException;

try {
    // Attempt to publish business
    $business = Business::find($businessId);
    $business->publish();
} catch (ApiResponseException $e) {
    if ($e->getCode() === 422) {
        // Handle business validation errors
        $responseData = $e->getResponseData();
        $errors = $responseData['error']['errors'] ?? [];

        if (isset($errors['name'])) {
            echo "Business name: " . implode(', ', $errors['name']) . "\n";
        }
        if (isset($errors['categories'])) {
            echo "Categories: " . implode(', ', $errors['categories']) . "\n";
        }
    }
}

Bulk Operations Error Handling

When performing bulk operations, you may want to handle partial failures:

use Engeni\ApiClient\Resource\CXM\User;
use Engeni\ApiClient\Exceptions\ApiResponseException;

$users = [1, 2, 3, 4, 5];
$successful = [];
$failed = [];

foreach ($users as $userId) {
    try {
        $user = User::find($userId);
        $user->setCurrentAccount(['account_id' => $newAccountId]);
        $successful[] = $userId;
    } catch (ApiResponseException $e) {
        $failed[$userId] = $e->getMessage();
    }
}

echo "Successfully updated: " . count($successful) . " users\n";
if (!empty($failed)) {
    echo "Failed to update: " . count($failed) . " users\n";
    foreach ($failed as $userId => $error) {
        echo "User $userId: $error\n";
    }
}

Error Logging and Monitoring

use Engeni\ApiClient\Exceptions\ApiResponseException;
use Engeni\ApiClient\Exceptions\ApiClientException;

try {
    // Your API operation
    $campaigns = Campaign::where('status', 'active')->get();
} catch (ApiResponseException $e) {
    // Log API errors for monitoring
    error_log("API Error: " . $e->getCode() . " - " . $e->getMessage());
    error_log("Response: " . json_encode($e->getResponseData()));

    // Handle the error appropriately for the user
    throw new \Exception("Unable to load campaigns. Please try again later.");
} catch (ApiClientException $e) {
    // Log client errors (network issues, etc.)
    error_log("Client Error: " . $e->getMessage());

    // Handle connectivity issues
    throw new \Exception("Connection error. Please check your internet connection.");
}

Advanced Usage

Custom Queries

// Create a custom query
$query = (new Query)
    ->setPath('/users')
    ->select('id', 'email')
    ->where('status', 'active')
    ->orderBy('created_at', 'DESC')
    ->limit(10);

// Execute the query
$response = $client->get($query);

Custom Actions

For non-CRUD operations, you can use the do method to call custom endpoints:

// Perform a custom action on a resource
User::do('ban', $userId);

// Search products
$results = Product::do('search', ['query' => 'keyword']);

// Perform operations with form parameters
$result = Resource::do('setup', [
    'verb' => 'POST',
    'form_params' => [
        'param1' => 'value1',
        'param2' => 'value2'
    ]
]);

// Custom operations on specific resources
$resource = Resource::find($resourceId);
$result = $resource->do('activate', [
    'verb' => 'POST'
]);

Application Context

You can set application context for a query, which is useful for multi-tenancy:

// Set context for a query
$users = User::withinContext([
    'account_id' => 1,
    'location_id' => 5
])->get();

RESTful API Conventions

The Engeni API follows RESTful conventions:

  1. Resources are named using plural nouns: /users, /accounts, /countries
  2. HTTP methods map to operations:
    • GET - Retrieve resources
    • POST - Create new resources
    • PUT - Update resources (full update)
    • PATCH - Partial update
    • DELETE - Remove resources
  3. Resource relations are accessed via nested paths or through embedding
  4. Custom actions use verb-based endpoints with POST method: /users/{id}/ban
  5. Snake_case is used for all field names in request and response payloads
  6. Date-time values follow RFC3339 standards and use UTC timezone

License

GNU GPLv3

Support

For support, please contact Engeni LLC.