PHPackages                             wjbecker/current-rms-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. wjbecker/current-rms-php

ActiveLibrary[API Development](/categories/api)

wjbecker/current-rms-php
========================

A framework-agnostic PHP library for the Current RMS API with fluent query builder, pagination, and collections

v1.2.0(4mo ago)08MITPHPPHP ^8.2

Since Jan 7Pushed 4mo agoCompare

[ Source](https://github.com/josh48202/current-rms-php)[ Packagist](https://packagist.org/packages/wjbecker/current-rms-php)[ RSS](/packages/wjbecker-current-rms-php/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (4)Versions (4)Used By (0)

Current RMS PHP Client
======================

[](#current-rms-php-client)

A framework-agnostic PHP library for interacting with the Current RMS API. Provides both a flexible generic HTTP client and typed endpoint classes for common operations.

```
composer require wjbecker/current-rms-php
```

Features
--------

[](#features)

- **Framework Agnostic**: Works standalone or with Laravel (zero required dependencies)
- **Fluent Query Builder**: Build queries with chainable methods instead of arrays
- **Memory-Efficient Pagination**: Generator-based cursor for processing large datasets
- **Custom Collection Class**: Lightweight collection with map, filter, reduce, and more
- **Paginator with Navigation**: Page metadata with next/previous navigation
- **Type-Safe DTOs**: Strongly-typed data objects with helper methods
- **Guzzle HTTP Client**: Built on Guzzle for reliable HTTP requests
- **Laravel Integration**: Optional service provider with auto-discovery and facade

---

Installation
------------

[](#installation)

```
composer require wjbecker/current-rms-php
```

---

Quick Start
-----------

[](#quick-start)

### Standalone Usage (No Laravel)

[](#standalone-usage-no-laravel)

```
use Wjbecker\CurrentRms\Client\Auth\ApiKeyAuth;
use Wjbecker\CurrentRms\Client\CurrentRmsClient;

// Create authentication
$auth = new ApiKeyAuth(
    subdomain: 'yourcompany',
    apiToken: 'your-api-token-here'
);

// Create client
$client = new CurrentRmsClient(
    baseUrl: 'https://api.current-rms.com/api/v1',
    auth: $auth
);

// Use the client
$opportunities = $client->opportunities()->list();
$opportunity = $client->opportunities()->find(123);
```

### Laravel Usage

[](#laravel-usage)

#### 1. Publish Configuration (Optional)

[](#1-publish-configuration-optional)

```
php artisan vendor:publish --tag=current-rms-config
```

#### 2. Configure Environment

[](#2-configure-environment)

Add to your `.env` file:

```
CURRENT_RMS_API_BASE_URL=https://api.current-rms.com/api/v1
CURRENT_RMS_AUTH_TYPE=api_key
CURRENT_RMS_SUBDOMAIN=yourcompany
CURRENT_RMS_API_TOKEN=your-api-token-here
```

#### 3. Use the Client

[](#3-use-the-client)

**Via Dependency Injection:**

```
use Wjbecker\CurrentRms\Client\CurrentRmsClient;

class OpportunityController extends Controller
{
    public function __construct(
        private CurrentRmsClient $client
    ) {}

    public function index()
    {
        $opportunities = $this->client->opportunities()->list();
        return view('opportunities.index', compact('opportunities'));
    }
}
```

**Via Facade:**

```
use Wjbecker\CurrentRms\Facades\CurrentRms;

$opportunities = CurrentRms::opportunities()->list();
$opportunity = CurrentRms::opportunities()->find(123);
```

---

Query Builder
-------------

[](#query-builder)

Build queries with a fluent interface instead of awkward arrays:

```
// Find confirmed opportunities for a specific member
$orders = $client->opportunities()->query()
    ->whereState(3)  // Confirmed
    ->forMember(456)
    ->createdAfter('2025-01-01')
    ->with('member', 'venue')
    ->get();

// Search by subject
$results = $client->opportunities()->query()
    ->whereContains('subject', 'Wedding')
    ->whereBetween('starts_at', '2025-06-01', '2025-08-31')
    ->get();

// Filter opportunity items
$rentalItems = $client->opportunityItems()->query()
    ->whereEquals('transaction_type', 1)  // Rentals
    ->forOpportunity(123)
    ->with('item')
    ->get();

// Complex queries with Ransack predicates
$items = $client->opportunityItems()->query()
    ->where('quantity', 'gteq', 5)           // quantity >= 5
    ->where('item_id', 'in', [1, 2, 3])      // item_id in array
    ->whereNotNull('charge_total')
    ->get();
```

### Available Query Methods

[](#available-query-methods)

```
// Comparison operators
->whereEquals('field', $value)
->whereNotEquals('field', $value)
->whereGreaterThan('field', $value)
->whereGreaterThanOrEqual('field', $value)
->whereLessThan('field', $value)
->whereLessThanOrEqual('field', $value)

// String matching
->whereContains('field', 'value')       // LIKE %value%
->whereContainsAll('field', ['a', 'b']) // Contains ALL values (AND)
->whereContainsAny('field', ['a', 'b']) // Contains ANY value (OR)
->whereNotContains('field', 'value')    // Does NOT contain
->whereStartsWith('field', 'value')
->whereNotStartsWith('field', 'value')
->whereEndsWith('field', 'value')
->whereNotEndsWith('field', 'value')
->whereMatches('field', '%pattern%')    // SQL LIKE pattern
->whereNotMatches('field', '%pattern%')

// Array/Null/Presence checks
->whereIn('field', [1, 2, 3])
->whereNotIn('field', [1, 2, 3])
->whereNull('field')
->whereNotNull('field')
->wherePresent('field')    // Not null AND not blank
->whereBlank('field')      // Null OR empty string
->whereTrue('field')
->whereFalse('field')

// Date ranges
->whereBetween('field', $start, $end)
->createdAfter('2025-01-01')
->createdBefore('2025-12-31')
->createdBetween('2025-01-01', '2025-12-31')
->updatedAfter('2025-01-01')

// Convenience methods
->whereState(3)           // Filter by state
->forMember($memberId)    // Filter by member
->forOpportunity($id)     // Filter by opportunity
->forItem($itemId)        // Filter by item

// Includes and pagination
->with('item', 'member')  // Include associations
->perPage(50)             // Set page size
```

### Complex Queries with AND/OR Logic

[](#complex-queries-with-andor-logic)

For more complex queries, use `whereOr()` and `whereAnd()` to group conditions:

```
// Simple OR: name contains "Bill" OR name contains "Fred"
$results = $client->opportunities()->query()
    ->whereOr(function ($or) {
        $or->whereContains('name', 'Bill');
        $or->whereContains('name', 'Fred');
    })
    ->get();

// Simple AND: state = 3 AND member_id = 123 (grouped)
$results = $client->opportunities()->query()
    ->whereAnd(function ($and) {
        $and->whereEquals('state', 3);
        $and->whereEquals('member_id', 123);
    })
    ->get();

// Complex: (name = "Bill" AND active = true) OR (name = "Fred" AND active = true)
$results = $client->opportunities()->query()
    ->whereOr(function ($or) {
        $or->group(function ($g) {
            $g->whereEquals('name', 'Bill');
            $g->whereTrue('active');
        });
        $or->group(function ($g) {
            $g->whereEquals('name', 'Fred');
            $g->whereTrue('active');
        });
    })
    ->get();

// Combine regular filters with grouped conditions
$results = $client->opportunities()->query()
    ->whereState(3)  // Regular filter (always applied)
    ->whereOr(function ($or) {
        $or->whereContains('subject', 'Wedding');
        $or->whereContains('subject', 'Corporate');
    })
    ->with('member')
    ->get();

// Date range OR queries
$results = $client->opportunities()->query()
    ->whereOr(function ($or) {
        $or->group(function ($g) {
            $g->whereBetween('starts_at', '2025-01-01', '2025-06-30');
        });
        $or->group(function ($g) {
            $g->whereBetween('starts_at', '2025-07-01', '2025-12-31');
        });
    })
    ->get();
```

**How it works:**

- Each `where*` call inside `whereOr()`/`whereAnd()` creates a separate condition group
- Groups are combined with OR (for `whereOr`) or AND (for `whereAnd`)
- Use `group()` to bundle multiple conditions that should be ANDed together within a group
- Regular filters (outside of whereOr/whereAnd) are always applied in addition to grouped conditions

### Query Execution Methods

[](#query-execution-methods)

```
// Get a Collection of results
$results = $query->get();

// Get just the first result
$item = $query->first();

// Get paginated results with metadata
$page = $query->paginate(1);

// Iterate through all pages (memory-efficient)
foreach ($query->cursor() as $item) {
    // Process one item at a time
}

// Check existence
$exists = $query->exists();

// Get count (requires API call)
$count = $query->count();
```

---

Pagination
----------

[](#pagination)

### Basic Pagination

[](#basic-pagination)

```
// Get a specific page
$page = $client->opportunities()->paginate(page: 1, perPage: 25);

// Access items on current page
foreach ($page->items() as $opportunity) {
    echo $opportunity->subject;
}

// Check pagination metadata
echo "Page {$page->currentPage()} of {$page->lastPage()}";
echo "Total items: {$page->total()}";
echo "Has more pages: " . ($page->hasMorePages() ? 'yes' : 'no');
```

### Page Navigation

[](#page-navigation)

```
// Navigate between pages
$nextPage = $page->nextPage();
$prevPage = $page->previousPage();
$specificPage = $page->goToPage(5);
```

### Memory-Efficient Iteration (Generators)

[](#memory-efficient-iteration-generators)

For large datasets, use the `cursor()` method which yields items one at a time:

```
// Process thousands of items without loading all into memory
foreach ($client->opportunityItems()->cursor() as $item) {
    echo "{$item->getItemName()} - Qty: {$item->quantity}\n";
    // Each item is fetched as needed, pages are loaded lazily
}

// With filters via query builder
foreach ($client->opportunityItems()->query()->forOpportunity(123)->cursor() as $item) {
    processItem($item);
}
```

### Pagination Limits

[](#pagination-limits)

- **Opportunities**: Max 25 items per page
- **Other Endpoints**: Max 100 items per page (default)

---

Collections
-----------

[](#collections)

The package includes a lightweight Collection class:

```
$opportunities = $client->opportunities()->list();

// Filtering
$confirmed = $opportunities->filter(fn($o) => $o->state === 3);
$drafts = $opportunities->where('state', 1);

// Mapping
$titles = $opportunities->map(fn($o) => $o->getTitle());
$subjects = $opportunities->pluck('subject');

// Aggregation
$total = $opportunities->sum('charge_total');
$average = $opportunities->avg('charge_total');

// Iteration
$opportunities->each(function($o) {
    echo $o->subject;
});

// Sorting
$sorted = $opportunities->sortBy('starts_at');

// Other operations
$first = $opportunities->first();
$last = $opportunities->last();
$count = $opportunities->count();
$chunk = $opportunities->take(5);

// Convert to array
$array = $opportunities->toArray();
```

---

Lazy Loading Relationships
--------------------------

[](#lazy-loading-relationships)

DTOs returned from endpoints support lazy loading of related resources:

```
// Get opportunities
$opportunities = $client->opportunities()->list();

foreach ($opportunities as $opportunity) {
    // Lazy load items for each opportunity (makes API call)
    $items = $opportunity->items()->get();

    foreach ($items as $item) {
        echo "{$item->name} - Qty: {$item->quantity}\n";
    }
}
```

### How It Works

[](#how-it-works)

When you retrieve data through endpoints (`list()`, `find()`, `create()`, etc.), the client reference is automatically injected into the DTOs. This enables the `items()` method to make API calls on your behalf.

```
// These all support lazy loading
$opportunity = $client->opportunities()->find(123);
$opportunity = $client->opportunities()->list()->first();
$opportunity = $client->opportunities()->query()->whereState(3)->first();

// Access items without knowing the opportunity ID
$items = $opportunity->items()->get();
$items = $opportunity->items()->query()->whereEquals('transaction_type', 1)->get();
```

### When Lazy Loading is NOT Available

[](#when-lazy-loading-is-not-available)

If you create a DTO manually without the client, lazy loading will throw an exception:

```
// Manual creation - no client available
$opportunity = OpportunityData::from(['id' => 123]);
$opportunity->items(); // Throws RuntimeException

// Use the explicit endpoint instead
$items = $client->opportunities()->items(123)->list();
```

---

API Reference
-------------

[](#api-reference)

### Opportunities Endpoint

[](#opportunities-endpoint)

```
// List all opportunities
$opportunities = $client->opportunities()->list();

// List with filters (legacy array syntax)
$opportunities = $client->opportunities()->list([
    'q[state_eq]' => 1
]);

// Find specific opportunity
$opportunity = $client->opportunities()->find(123);

// Find with includes
$opportunity = $client->opportunities()->find(123, ['owner', 'member']);

// Create opportunity
$opportunity = $client->opportunities()->create([
    'subject' => 'New Project',
    'member_id' => 456,
    'starts_at' => '2025-01-15T08:00:00.000Z',
    'ends_at' => '2025-01-16T18:00:00.000Z',
]);

// Update opportunity
$opportunity = $client->opportunities()->update(123, [
    'subject' => 'Updated Project Name'
]);

// Delete opportunity
$client->opportunities()->destroy(123);

// Checkout (convert to order)
$opportunity = $client->opportunities()->checkout([
    'opportunity_id' => 123
]);

// Clone opportunity
$newOpportunity = $client->opportunities()->clone(123);

// Finalize check-in
$opportunity = $client->opportunities()->finalizeCheckIn(123, [
    'return' => [
        'return_at' => '2025-01-15T18:00:00.000Z'
    ],
    'move_outstanding' => false,
    'complete_sales_items' => false,
]);
```

### Opportunity Items Endpoint

[](#opportunity-items-endpoint)

```
// List all opportunity items
$items = $client->opportunityItems()->list();

// List with includes (item data, assets, etc.)
$items = $client->opportunityItems()->list([], ['item', 'item_assets']);

// List items for specific opportunity (scoped)
$items = $client->opportunities()->items(123)->list();

// Find specific item
$item = $client->opportunities()->items(123)->find(456);

// Find with includes
$item = $client->opportunities()->items(123)->find(456, ['item', 'rate_definition']);

// Create item
$item = $client->opportunities()->items(123)->create([
    'item_id' => 789,
    'quantity' => 5
]);

// Update item
$item = $client->opportunities()->items(123)->update(456, [
    'quantity' => 10
]);

// Delete item
$client->opportunities()->items(123)->destroy(456);
```

### Generic HTTP Methods

[](#generic-http-methods)

```
// GET request
$data = $client->get('/endpoint', ['param' => 'value']);

// POST request
$data = $client->post('/endpoint', ['key' => 'value']);

// PUT request
$data = $client->put('/endpoint', ['key' => 'value']);

// PATCH request
$data = $client->patch('/endpoint', ['key' => 'value']);

// DELETE request
$client->delete('/endpoint');
```

---

Configuration Options
---------------------

[](#configuration-options)

When creating the client manually:

```
$client = new CurrentRmsClient(
    baseUrl: 'https://api.current-rms.com/api/v1',  // required
    auth: $auth,                                      // optional
    timeout: 30,                                      // optional, default: 30
    connectTimeout: 10,                               // optional, default: 10
    verifySsl: true                                   // optional, default: true
);
```

---

Data Objects
------------

[](#data-objects)

All endpoints return strongly-typed Data Transfer Objects (DTOs) with helper methods.

### OpportunityData

[](#opportunitydata)

```
// Properties (all nullable)
$opportunity->id;
$opportunity->subject;
$opportunity->state;              // State ID (1=draft, 2=quote, 3=order, etc.)
$opportunity->state_name;         // "Draft", "Quote", "Order", etc.
$opportunity->status;
$opportunity->starts_at;
$opportunity->ends_at;
$opportunity->charge_total;
$opportunity->opportunity_items;  // Array of OpportunityItemData (if included)

// Helper methods
$opportunity->isDraft();          // Check if in draft state
$opportunity->isOpen();           // Check if status is open
$opportunity->getTitle();         // Get subject/title
$opportunity->getMemberName();    // Get customer name from nested data
$opportunity->getOwnerName();     // Get owner name from nested data
$opportunity->getCustomField('key'); // Get custom field value

// Lazy loading (when retrieved via endpoint)
$opportunity->items();            // Get ScopedOpportunityItemsEndpoint
$opportunity->items()->get();     // Fetch all items
$opportunity->items()->query()->whereEquals('transaction_type', 1)->get();
```

### OpportunityItemData

[](#opportunityitemdata)

```
// Properties (all nullable)
$item->id;
$item->opportunity_id;
$item->item_id;               // Product/Item ID
$item->transaction_type;      // 1=Rental, 2=Sale, 3=Service
$item->quantity;
$item->name;
$item->charge;
$item->item;                  // ItemData object (if included)

// Helper methods
$item->isRental();            // Check if rental (transaction_type === 1)
$item->isSale();              // Check if sale (transaction_type === 2)
$item->isService();           // Check if service (transaction_type === 3)
$item->getItemName();         // Get item name (from item or name field)
$item->getItemBarcode();      // Get barcode (from item or sku field)
$item->getCustomField('key'); // Get custom field value
```

### ItemData (Product/Item)

[](#itemdata-productitem)

```
// Available when including 'item' association
$item = $opportunityItem->item;

// Properties
$item->id;
$item->name;
$item->description;
$item->barcode;
$item->active;
$item->replacement_charge;
$item->weight;

// Helper methods
$item->isActive();
$item->isAccessoryOnly();
$item->isDiscountable();
$item->getProductGroupName();
$item->getTaxClassName();
$item->getIconUrl();
$item->getCustomField('key');
```

---

Testing
-------

[](#testing)

```
./vendor/bin/pest
```

---

Package Structure
-----------------

[](#package-structure)

```
.
├── src/
│   ├── Client/
│   │   ├── CurrentRmsClient.php
│   │   ├── Auth/
│   │   │   ├── AuthManager.php
│   │   │   ├── ApiKeyAuth.php
│   │   │   └── TokenStorage.php
│   │   └── Exceptions/
│   ├── Data/
│   │   ├── OpportunityData.php
│   │   ├── OpportunityItemData.php
│   │   └── ItemData.php
│   ├── Endpoints/
│   │   ├── BaseEndpoint.php
│   │   ├── OpportunitiesEndpoint.php
│   │   ├── OpportunityItemsEndpoint.php
│   │   └── ScopedOpportunityItemsEndpoint.php
│   ├── Query/
│   │   ├── QueryBuilder.php
│   │   └── GroupBuilder.php
│   ├── Support/
│   │   ├── Collection.php
│   │   └── Paginator.php
│   ├── Facades/
│   │   └── CurrentRms.php
│   └── CurrentRmsServiceProvider.php
├── config/
│   └── current-rms.php
├── tests/
├── LICENSE
├── README.md
└── composer.json

```

---

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for more information.

---

Credits
-------

[](#credits)

- **Author**: William Becker
- **Package**: wjbecker/current-rms-php

###  Health Score

35

—

LowBetter than 80% of packages

Maintenance76

Regular maintenance activity

Popularity4

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

129d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1b88b7396306b1a0d5f8d6f82ab00118b66c1fe5d057695e687e7daec104b155?d=identicon)[josh48202](/maintainers/josh48202)

---

Tags

apiclientpaginationcollectionquery builderrentalinventorycurrent-rms

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/wjbecker-current-rms-php/health.svg)

```
[![Health](https://phpackages.com/badges/wjbecker-current-rms-php/health.svg)](https://phpackages.com/packages/wjbecker-current-rms-php)
```

###  Alternatives

[openai-php/laravel

OpenAI PHP for Laravel is a supercharged PHP API client that allows you to interact with the Open AI API

3.7k7.6M74](/packages/openai-php-laravel)[resend/resend-php

Resend PHP library.

564.7M21](/packages/resend-resend-php)[crowdin/crowdin-api-client

PHP client library for Crowdin API v2

611.5M5](/packages/crowdin-crowdin-api-client)[mozex/anthropic-laravel

Anthropic PHP for Laravel is a supercharged PHP API client that allows you to interact with the Anthropic API

71226.4k1](/packages/mozex-anthropic-laravel)[markrogoyski/numverify-api-client-php

Numverify API Client for PHP

1220.9k](/packages/markrogoyski-numverify-api-client-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
