blashbrook / papiclient
Provides Client for Polaris ILS API
Requires
- php: ^8.2
- illuminate/database: ^10.0
- illuminate/support: ^10.0
- league/commonmark: ^2.4.0
- nette/schema: ^v1.3.0
Requires (Dev)
- livewire/livewire: ^3.0
- orchestra/testbench: ^v8.22.2
- phpunit/phpunit: ~10
- symfony/console: ^v6.4.6
- symfony/css-selector: ^v7.0.3
- symfony/event-dispatcher: ^v7.0.3
- symfony/stopwatch: v7.3.0
- symfony/string: ^v7.0.3
- dev-master
- v2.3.0
- v2.2.1
- v2.2.0
- v2.1.0
- v2.0.2
- v2.0.1
- v2.0.0
- v1.5.0
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.19
- v1.3.18
- v1.3.17
- v1.3.16
- v1.3.15
- v1.3.14
- v1.3.13
- v1.3.12
- v1.3.11
- v1.3.10
- v1.3.9
- v1.3.8
- v1.3.7
- v1.3.6
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.3-alpha
- v1.0.2
- v1.0.1
- dev-dev-master
This package is auto-updated.
Last update: 2025-10-01 21:33:16 UTC
README
A Laravel package for integrating with Polaris API (PAPI) services. Provides API client functionality and pre-built Livewire components for common library operations.
Key Features:
- Fluent API client for Polaris ILS integration
- Pre-built Livewire components (Delivery Options, etc.)
- Session management and user preference handling
- Comprehensive testing suite
Take a look at contributing.md to see a to do list.
Table of Contents
Installation
Via Composer
$ composer require blashbrook/papiclient
Add the following variables to the project .env file
# Access ID found under PAPI Key Management in the Polaris Web Admin Tool i.e. https://catalog.yourlibrary.org/webadmin/PAPIKeyManagement.aspx PAPI_ACCESS_ID= # Access Key found under PAPI Key Management in the Polaris Web Admin Tool i.e. https://catalog.yourlibrary.org/webadmin/PAPIKeyManagement.aspx PAPI_ACCESS_KEY= # Polaris API base URL i.e. https://catalog/yourlibrary.org/PAPIService/REST PAPI_BASE_URL= # default PAPI_PROTECTED_SCOPE=protected # default PAPI_PUBLIC_SCOPE=public # default PAPI_VERSION=v1 # default PAPI_LANGID=1033 # default PAPI_APPID=100 # default PAPI_ORGID=3 # default protected PAPI URL constructor PAPI_PROTECTED_URI="${PAPI_BASE_URL}/${PAPI_PROTECTED_SCOPE}/${PAPI_VERSION}/${PAPI_LANGID}/${PAPI_APPID}/${PAPI_ORGID}/" # default public PAPI URL constructor PAPI_PUBLIC_URI="${PAPI_BASE_URL}/${PAPI_PUBLIC_SCOPE}/${PAPI_VERSION}/${PAPI_LANGID}/${PAPI_APPID}/${PAPI_ORGID}/" # Polaris branch ID found in Polaris under Administration > Explorer > Branches # Right-click branch name and select Properties > About PAPI_LOGONBRANCHID= # Search under Administration > Staff Member in Polaris # Right-click staff member name and select Properties > About PAPI_LOGONUSERID= # Search under Administration > Workstation in Polaris # Right-click workstation name and select Properties > About PAPI_LOGONWORKSTATIONID= # Active Directory Domain used to log into Polaris i.e. Domain\Username PAPI_DOMAIN= # Polaris username (or if no Domain, user's email address) PAPI_STAFF= # Polaris user password in double quotes PAPI_PASSWORD= # Email to receive staff notifications in double quotes PAPI_ADMIN_EMAIL="ecard@dcplibrary.org" # Display name for staff email in double quotes PAPI_ADMIN_NAME=
Usage
- Use Injection to instantiate PAPIClient in a class:
use Blashbrook\PAPIClient\PAPIClient;
protected PAPIClient $papiclient;
public function __construct(PAPIClient $papiclient) {
$this->papiclient = $papiclient;
}
- In a Livewire component, use the boot method:
use Blashbrook\PAPIClient\PAPIClient;
protected PAPIClient $papiclient;
public function boot(PAPIClient $papiclient) {
$this->papiclient = $papiclient;
}
- To make an API call to your Polaris server:
// Validate PAPI Access Key
$response = $this->papiclient->method('GET')->uri('apikeyvalidate')->execRequest();
- functions include:
- method('GET|PUT')
- protected() // Uses the protected API base URI instead of the default public URI.
- patron('BARCODE') // Allows you to insert a patron's barcode into the URI.
- uri('API Endpoint') // The part of the URI that performs the desired function (i.e 'authenicator/patron' or 'apikeyvalidate').
- params(array) // Used for form submissions (i.e. ['Barcode'=>'55555555555555', 'Password'=> '1234'] is sent to log in a patron).
- auth('AccessSecret') // Inserts a patron's temporary authentication token in the request headers.
- MORE TO COME!
Components
DeliveryOptionSelectFlux
A Livewire component that provides a filtered, customizable select dropdown for delivery options using the Flux UI framework.
Features
- Filtered Options: Only displays allowed delivery options from your database
- Custom Display Names: Override database values with user-friendly labels
- Session Integration: Remembers user's selection across page visits
- Flux UI Integration: Seamlessly works with Flux select components
- Two-way Data Binding: Integrates with parent Livewire components via
wire:model
Quick Start
To use the delivery options component with session integration:
- In your Livewire component:
class YourComponent extends Component { public $deliveryOptionIDChanged; public function mount() { $this->deliveryOptionIDChanged = session('DeliveryOptionID', 8); } public function updatedDeliveryOptionIDChanged($value) { session(['DeliveryOptionID' => $value]); } }
- In your Blade template:
<livewire:delivery-option-select-flux wire:model="deliveryOptionIDChanged" :delivery-option-i-d-changed="$deliveryOptionIDChanged" />
That's it! The component will show: Mail, Email, Phone, Text Messaging with session persistence.
Important: The component includes an updatedDeliveryOptionIDChanged()
listener that automatically:
- Updates the session with the new selection
- Dispatches a
deliveryOptionUpdated
event to notify parent components
Configuration
1. Available Delivery Options
The component filters delivery options using an internal array. To modify which options are shown and their display names, edit the $availableDeliveryOptions
array in /src/Livewire/DeliveryOptionSelectFlux.php
:
private $availableDeliveryOptions = [ 'Mailing Address' => 'Mail', // Database value => Display name 'Email Address' => 'Email', // Database value => Display name 'Phone 1' => 'Phone', // Database value => Display name 'TXT Messaging' => 'Text Messaging' // Database value => Display name ];
Key Points:
- Database values (keys) must exactly match the
DeliveryOption
field in your database - Display names (values) are what users see in the dropdown
- Only options listed in this array will appear in the select dropdown
- Any delivery options in your database NOT in this array will be filtered out
2. Usage in Blade Templates
Basic Usage:
<livewire:delivery-option-select-flux wire:model="deliveryOptionIDChanged" />
With Initial Value:
<livewire:delivery-option-select-flux wire:model="deliveryOptionIDChanged" :delivery-option-i-d-changed="$initialValue" />
3. Parent Component Integration
In your parent Livewire component:
class YourParentComponent extends Component { public $deliveryOptionIDChanged; public function mount() { // Set from session (recommended) $this->deliveryOptionIDChanged = session('DeliveryOptionID', 8); // OR set from user preference // $this->deliveryOptionIDChanged = auth()->user()->preferred_delivery_option ?? 8; // OR set hardcoded default // $this->deliveryOptionIDChanged = 8; } // Optional: Update session when value changes public function updatedDeliveryOptionIDChanged($value) { session(['DeliveryOptionID' => $value]); } }
4. Database Requirements
Ensure your delivery_options
table has the structure:
// Migration example Schema::create('delivery_options', function (Blueprint $table) { $table->id(); $table->integer('DeliveryOptionID')->unique(); $table->string('DeliveryOption'); $table->timestamps(); });
Sample Data:
// Seeder example DeliveryOption::create(['DeliveryOptionID' => 1, 'DeliveryOption' => 'Mailing Address']); DeliveryOption::create(['DeliveryOptionID' => 2, 'DeliveryOption' => 'Email Address']); DeliveryOption::create(['DeliveryOptionID' => 3, 'DeliveryOption' => 'Phone 1']); DeliveryOption::create(['DeliveryOptionID' => 8, 'DeliveryOption' => 'TXT Messaging']);
5. Customization Examples
Adding New Options:
- Add the option to your database
- Add it to the
$availableDeliveryOptions
array:private $availableDeliveryOptions = [ 'Mailing Address' => 'Mail', 'Email Address' => 'Email', 'Phone 1' => 'Phone', 'TXT Messaging' => 'Text Messaging', 'Push Notification' => 'Push Alerts', // New option ];
Changing Display Names:
private $availableDeliveryOptions = [ 'Mailing Address' => 'Postal Mail', // Changed from 'Mail' 'Email Address' => 'Electronic Mail', // Changed from 'Email' 'Phone 1' => 'Voice Call', // Changed from 'Phone' 'TXT Messaging' => 'SMS', // Changed from 'Text Messaging' ];
Removing Options:
Simply remove the line from the $availableDeliveryOptions
array. The option will be filtered out even if it exists in the database.
6. Session Integration
The component automatically integrates with Laravel sessions:
- Reading: Gets initial value from
session('DeliveryOptionID', defaultValue)
- Writing: Updates session when user changes selection (if parent component implements
updatedDeliveryOptionIDChanged()
) - Persistence: User's choice persists across browser sessions
7. Testing
The component includes comprehensive tests. Run them with:
php artisan test --filter=DeliveryOptionSelectFluxTest
8. Troubleshooting
Component not showing options:
- Verify database has delivery options with exact names matching
$availableDeliveryOptions
keys - Check that the
DeliveryOption
model is accessible - Ensure database connection is working
Trim error:
- This should be resolved, but if it occurs, clear view cache:
php artisan view:clear
Session not persisting:
- Ensure
updatedDeliveryOptionIDChanged()
method is implemented in parent component - Verify Laravel session configuration is correct
PatronUDFSelectFlux
A dynamic Livewire component that creates select dropdowns from Patron User Defined Fields (UDFs) stored in your database. Perfect for forms that need library-specific custom fields like School, Department, Grade Level, etc.
Features
- Dynamic UDF Loading: Automatically loads options from PatronUdf database records
- External Label Selection: Specify which UDF to use via the
patronUdfLabel
parameter - Session Integration: Remembers user's selection with label-specific session keys
- Flux UI Integration: Modern, accessible UI components
- Custom Display Names: Override database values with user-friendly labels
- Event Broadcasting: Notifies parent components of selection changes
- Automatic Listener: Includes
updatedSelectedPatronUDFChanged()
for external variable updates
Quick Start
To use the PatronUDF component for a "School" selection:
- In your Livewire component:
class YourComponent extends Component { public $selectedSchool; public function mount() { $this->selectedSchool = session('PatronUDF_School', ''); } #[On('patronUdfUpdated')] public function handleUdfUpdate($data) { if ($data['label'] === 'School') { $this->selectedSchool = $data['value']; // Handle school selection logic here } } }
- In your Blade template:
<livewire:patron-udf-select-flux wire:model="selectedSchool" :patron-udf-label="'School'" :selected-patron-udf-changed="$selectedSchool" />
Important: The component includes an updatedSelectedPatronUDFChanged()
listener that automatically:
- Updates the label-specific session (
PatronUDF_School
, etc.) - Dispatches a
patronUdfUpdated
event to notify parent components - Supports two-way data binding with external variables
PostalCodeSelectFlux
A comprehensive Livewire component for postal code selection with city, state, and county information. Ideal for address forms, service area selection, and location-based features.
Features
- Rich Location Data: Shows city, state, postal code, and county information
- Multiple Display Formats: Customizable display formats (full, city_state_zip, city_zip, etc.)
- Geographic Filtering: Filter by state, county, or other geographic criteria
- Session Integration: Remembers user's postal code selection
- Flux UI Integration: Modern, accessible select component
- Event Broadcasting: Dispatches detailed postal code information
- Search Functionality: Built-in search and filtering capabilities
- Automatic Listener: Includes
updatedSelectedPostalCodeChanged()
for external variable updates
Quick Start
To use the postal code component for address selection:
- In your Livewire component:
class AddressComponent extends Component { public $selectedPostalCode; public $userCity; public $userState; public function mount() { $this->selectedPostalCode = session('PostalCodeID', null); } #[On('postalCodeUpdated')] public function handlePostalCodeUpdate($data) { $this->userCity = $data['city']; $this->userState = $data['state']; // Auto-populate address fields $this->updateAddressFromPostalCode($data); } private function updateAddressFromPostalCode($postalData) { // Handle postal code selection logic $this->dispatch('addressUpdated', $postalData); } }
- In your Blade template:
<livewire:postal-code-select-flux wire:model="selectedPostalCode" :selected-postal-code-changed="$selectedPostalCode" display-format="city_state_zip" />
Important: The component includes an updatedSelectedPostalCodeChanged()
listener that automatically:
- Updates the session with the new postal code ID
- Dispatches a comprehensive
postalCodeUpdated
event with all location data - Supports two-way data binding with external variables
Listener Implementation Details
All Flux components include automatic listeners that:
- Update external variables when selections change (enabling
wire:model
binding) - Persist selections in session storage
- Dispatch events to notify parent components
- Handle empty/null values gracefully
- Support real-time updates without page refresh
Example of listener in action:
// When user selects a new postal code: // 1. updatedSelectedPostalCodeChanged() fires automatically // 2. Session is updated: session(['PostalCodeID' => $newValue]) // 3. Event is dispatched: postalCodeUpdated with full location data // 4. Parent component receives event and can update other fields
For complete configuration and usage examples, see the comprehensive testing documentation below.
Change log
PAPIClient has been refactored to use fluency! Now you can chain commands together, making the client more flexible and easier to use.
Please see the changelog for more information on what has changed recently.
@TODO Add Error catching
Testing
PAPIClient includes a comprehensive test suite covering unit tests, integration tests, and performance tests. The package uses PHPUnit 10 with organized test suites and convenient make commands.
Quick Start
# Run unit tests (safe, no API calls) make test # Run all tests make test-all # Generate coverage report make test-coverage
Test Suites
Unit Tests
Fast, isolated tests that don't make real API calls:
# Via make command (recommended) make test-unit # Via PHPUnit directly vendor/bin/phpunit --testsuite=Unit
Unit tests cover:
- PAPIClient instantiation and configuration
- Method chaining and fluent interface
- HTTP request building
- Response handling and error management
- Internal state management
- Mock API responses
Integration Tests
Tests that make real API calls to your PAPI server:
# Via make command (requires environment setup) make test-integration # Via PHPUnit directly ENABLE_INTEGRATION_TESTS=true vendor/bin/phpunit --testsuite=Integration
Integration tests cover:
- Real API connectivity
- Authentication flows
- Error handling with live API responses
- Network timeout scenarios
- Different HTTP methods
Prerequisites for Integration Tests: Set these environment variables before running integration tests:
export PAPI_ACCESS_ID='your_access_id' export PAPI_ACCESS_KEY='your_access_key' export PAPI_BASE_URL='https://your-catalog.org/PAPIService/REST' export PAPI_PUBLIC_URI='https://your-catalog.org/PAPIService/REST/public/v1/1033/100/1/' export PAPI_PROTECTED_URI='https://your-catalog.org/PAPIService/REST/protected/v1/1033/100/1/' export ENABLE_INTEGRATION_TESTS=true
Or use the setup helper:
make setup-integration
Feature Tests
High-level tests for Laravel integration:
make test-feature
Performance Tests
Benchmark tests for response times and memory usage:
make test-performance
Coverage Reports
Generate detailed code coverage reports:
# HTML coverage report (requires Xdebug) make test-coverage # Opens coverage-report/index.html # Clover XML format (for CI/CD) make test-coverage-clover
Make Commands Reference
All available testing commands via Makefile:
make help # Show all available commands make test # Run unit tests (default) make test-unit # Run unit tests only make test-integration # Run integration tests make test-feature # Run feature tests make test-all # Run all test suites make test-coverage # Generate HTML coverage make test-performance # Run performance tests make test-real-api # Integration tests with confirmation
Direct PHPUnit Usage
If you prefer using PHPUnit directly:
# All tests vendor/bin/phpunit # Specific test suite vendor/bin/phpunit --testsuite=Unit vendor/bin/phpunit --testsuite=Integration vendor/bin/phpunit --testsuite=Feature # Specific test file vendor/bin/phpunit Tests/Unit/PAPIClientTest.php # Specific test method vendor/bin/phpunit --filter testCanInstantiateClient # With coverage vendor/bin/phpunit --coverage-html coverage-report
Continuous Integration
For CI/CD pipelines, use:
make ci # Runs all tests and code style checks
Testing Configuration
The test suite uses phpunit.xml
with separate configurations for:
- Test environment variables
- Database settings (using array drivers for speed)
- Source code coverage filtering
- Test suite organization
Test Safety
Important: Integration tests are disabled by default to prevent accidental API calls. They only run when explicitly enabled via environment variables.
- ✅ Unit tests: Always safe to run (no network calls)
- ✅ Feature tests: Safe (use Laravel testing features)
- ⚠️ Integration tests: Require real API credentials
- ✅ Performance tests: Safe (use mocked responses)
Component Testing
The package includes comprehensive tests for all Livewire components (DeliveryOptionSelectFlux, PatronUDFSelectFlux, PostalCodeSelectFlux).
Component Test Structure
Tests/
├── Unit/
│ ├── PatronUDFSelectFluxTest.php # Unit tests for PatronUDF component
│ ├── PostalCodeSelectFluxTest.php # Unit tests for PostalCode component
│ └── PAPIClientTest.php # Unit tests for API client
├── Feature/
│ ├── LivewireComponentsTest.php # Feature tests for all components
│ └── PAPIClientTest.php # Feature tests for API client
└── Integration/
└── PAPIClientIntegrationTest.php # Integration tests with real API
Running Component Tests
All component tests:
# Run all component-related tests vendor/bin/phpunit --filter "Component|Livewire" # Via make command make test-components # If you add this to Makefile
Individual component tests:
# PatronUDFSelectFlux tests vendor/bin/phpunit Tests/Unit/PatronUDFSelectFluxTest.php vendor/bin/phpunit --filter PatronUDFSelectFlux # PostalCodeSelectFlux tests vendor/bin/phpunit Tests/Unit/PostalCodeSelectFluxTest.php vendor/bin/phpunit --filter PostalCodeSelectFlux # All Livewire component feature tests vendor/bin/phpunit Tests/Feature/LivewireComponentsTest.php
Test specific component features:
# Test only session integration vendor/bin/phpunit --filter "session|Session" # Test only event dispatching vendor/bin/phpunit --filter "event|Event|dispatch" # Test only filtering and search vendor/bin/phpunit --filter "filter|Filter|search"
Component Test Coverage
PatronUDFSelectFlux Tests Cover:
- Component instantiation and initialization
- UDF loading based on external label parameter
- Label-specific session management (
PatronUDF_{Label}
) - Event dispatching (
patronUdfUpdated
) - Option filtering and display name customization
- Edge cases (empty values, non-existent labels, whitespace handling)
- Multiple component instances working independently
- View rendering and Flux UI integration
PostalCodeSelectFlux Tests Cover:
- Component instantiation and configuration
- Postal code loading with geographic filtering
- Multiple display formats (full, city_state_zip, city_zip)
- Search and filtering functionality
- Session persistence (
PostalCodeID
) - Event dispatching with comprehensive location data
- Database query ordering and optimization
- Edge cases (empty filters, invalid formats)
- Performance considerations for large datasets
Feature Tests Cover:
- Cross-component integration and independence
- Real database interactions with migrations
- Session state persistence across "page loads"
- Component coexistence without interference
- Flux UI template rendering
- Error handling with missing database data
Test Database Setup
Component tests use in-memory SQLite databases with the RefreshDatabase
trait:
// Tests automatically create test data PatronUdf::create([ 'PatronUdfID' => 1, 'Label' => 'School', 'Display' => true, 'Values' => 'Elementary School,Middle School,High School,College', 'Required' => true ]); PostalCode::create([ 'PostalCodeID' => 1, 'PostalCode' => '80202', 'City' => 'Denver', 'State' => 'CO', 'County' => 'Denver County', 'CountryID' => 1 ]);
Testing Component Interactions
Test Event Handling:
// Example from tests - verifying event dispatch $component->set('selectedPatronUDFChanged', 'College'); $component->assertDispatched('patronUdfUpdated', [ 'label' => 'School', 'value' => 'College', 'displayName' => 'College' ]);
Test Session Integration:
// Verify session persistence $component->set('selectedPostalCodeChanged', 1); $this->assertEquals(1, Session::get('PostalCodeID')); // Verify session loading on component mount Session::put('PatronUDF_School', 'High School'); $component = Livewire::test(PatronUDFSelectFlux::class, [ 'patronUdfLabel' => 'School' ]); $this->assertEquals('High School', $component->get('selectedPatronUDFChanged'));
Test Component Properties:
// Test dynamic option loading $options = $component->get('options'); $this->assertCount(4, $options); // Test filtering functionality $component->call('filterOptions', 'Denver'); $filteredOptions = $component->get('filteredOptions'); $this->assertCount(1, $filteredOptions);
Running Tests with Coverage
# Component test coverage vendor/bin/phpunit --coverage-html coverage-components --filter "Component|Livewire" # Full coverage including components make test-coverage
Component Test Performance
Test execution speed:
- Unit tests: ~50-100ms per test (no database I/O)
- Feature tests: ~200-500ms per test (includes database operations)
- Full component test suite: ~5-10 seconds
Optimizing test performance:
# Run tests in parallel (if available) vendor/bin/phpunit --parallel 4 # Run only fast unit tests during development vendor/bin/phpunit --testsuite=Unit --filter Component
Troubleshooting Tests
Tests not found:
composer dump-autoload
Integration tests skipped:
Ensure ENABLE_INTEGRATION_TESTS=true
is set
Component tests failing:
# Clear Laravel caches php artisan cache:clear php artisan view:clear php artisan config:clear # Ensure database migrations are up to date php artisan migrate:refresh --env=testing
Livewire component tests failing:
# Verify Livewire is properly installed composer show livewire/livewire # Check component registration php artisan livewire:list
Coverage requires Xdebug:
# Install Xdebug via PECL or package manager
pecl install xdebug
Permission errors:
sudo chown -R $(whoami):$(whoami) storage/ chmod -R 755 storage/
Database-related test failures:
# Ensure test database is properly configured # Check phpunit.xml database settings # Verify RefreshDatabase trait is being used
PatronUDFSelectFlux
A dynamic Livewire component that creates select dropdowns from Patron User Defined Fields (UDFs) stored in your database. Perfect for forms that need library-specific custom fields like School, Department, Grade Level, etc.
Features
- Dynamic UDF Loading: Automatically loads options from PatronUdf database records
- External Label Selection: Specify which UDF to use via the
patronUdfLabel
parameter - Session Integration: Remembers user's selection with label-specific session keys
- Flux UI Integration: Modern, accessible UI components
- Custom Display Names: Override database values with user-friendly labels
- Event Broadcasting: Notifies parent components of selection changes
Quick Start
To use the PatronUDF component for a "School" selection:
- In your Livewire component:
class YourComponent extends Component { public $selectedSchool; public function mount() { $this->selectedSchool = session('PatronUDF_School', ''); } #[On('patronUdfUpdated')] public function handleUdfUpdate($data) { if ($data['label'] === 'School') { $this->selectedSchool = $data['value']; // Handle school selection logic here } } }
- In your Blade template:
<livewire:patron-udf-select-flux wire:model="selectedSchool" :patron-udf-label="'School'" :selected-patron-udf-changed="$selectedSchool" />
Configuration
1. Database Setup
Ensure your patron_udfs
table has records like:
// Migration example Schema::create('patron_udfs', function (Blueprint $table) { $table->id(); $table->integer('PatronUdfID')->unique(); $table->string('Label'); // e.g., 'School', 'Department', 'Grade' $table->boolean('Display')->default(true); $table->text('Values')->nullable(); // Comma-separated values $table->boolean('Required')->default(false); $table->string('DefaultValue')->nullable(); $table->timestamps(); });
Sample Data:
// Seeder example PatronUdf::create([ 'PatronUdfID' => 1, 'Label' => 'School', 'Display' => true, 'Values' => 'Elementary School,Middle School,High School,College,Adult Education', 'Required' => true ]); PatronUdf::create([ 'PatronUdfID' => 2, 'Label' => 'Department', 'Display' => true, 'Values' => 'Math,Science,English,History,Art,Music', 'Required' => false ]);
2. Component Parameters
<livewire:patron-udf-select-flux :patron-udf-label="'School'" {{-- Required: UDF Label to use --}} wire:model="selectedValue" {{-- Two-way data binding --}} :selected-patron-udf-changed="$currentValue" {{-- Initial value --}} :placeholder="'Choose your school'" {{-- Custom placeholder --}} :attrs="['class' => 'custom-class']" {{-- Additional HTML attributes --}} />
3. Usage Examples
Basic School Selection:
<livewire:patron-udf-select-flux patron-udf-label="School" />
Department Selection with Custom Placeholder:
<livewire:patron-udf-select-flux patron-udf-label="Department" placeholder="Select your department" wire:model="userDepartment" />
Grade Level with Session Integration:
// In your component public function mount() { $this->selectedGrade = session('PatronUDF_Grade', ''); } public function updatedSelectedGrade($value) { session(['PatronUDF_Grade' => $value]); }
<livewire:patron-udf-select-flux patron-udf-label="Grade" wire:model="selectedGrade" :selected-patron-udf-changed="$selectedGrade" />
4. Event Handling
The component dispatches patronUdfUpdated
events:
#[On('patronUdfUpdated')] public function handlePatronUdfUpdate($data) { // $data contains: // - 'label': The UDF label (e.g., 'School') // - 'value': Selected value (e.g., 'High School') // - 'displayName': Display name (customizable) match($data['label']) { 'School' => $this->updateSchoolPreferences($data['value']), 'Department' => $this->updateDepartmentSettings($data['value']), default => null }; }
5. Session Integration
- Auto-session keys:
PatronUDF_{Label}
(e.g.,PatronUDF_School
) - Persistent selection: User choices persist across browser sessions
- Label-specific: Different UDFs maintain separate session values
6. Customization
Custom Display Names (Advanced): Extend the component to override display names:
// Create a custom component extending PatronUDFSelectFlux class CustomSchoolSelectFlux extends PatronUDFSelectFlux { protected function getCustomDisplayName(string $value): string { return match($value) { 'Elementary School' => '🏫 Elementary (K-5)', 'Middle School' => '🏛️ Middle School (6-8)', 'High School' => '🎓 High School (9-12)', 'College' => '🏛️ College/University', default => $value }; } }
7. Troubleshooting
No options appearing:
- Verify PatronUdf record exists with the specified Label
- Check that
Display
field istrue
- Ensure
Values
field contains comma-separated options - Confirm database connection is working
Session not persisting:
- Verify Laravel session configuration
- Check session driver settings
- Ensure session middleware is active
Wrong UDF loading:
- Double-check the
patronUdfLabel
parameter spelling - Verify Label field in database matches exactly (case-sensitive)
- Check for duplicate Label entries in database
PostalCodeSelectFlux
A comprehensive Livewire component for postal code selection with city, state, and county information. Ideal for address forms, service area selection, and location-based features.
Features
- Rich Location Data: Shows city, state, postal code, and county information
- Multiple Display Formats: Customizable display formats (full, city_state_zip, city_zip, etc.)
- Geographic Filtering: Filter by state, county, or other geographic criteria
- Session Integration: Remembers user's postal code selection
- Flux UI Integration: Modern, accessible select component
- Event Broadcasting: Dispatches detailed postal code information
- Search Functionality: Built-in search and filtering capabilities
Quick Start
To use the postal code component for address selection:
- In your Livewire component:
class AddressComponent extends Component { public $selectedPostalCode; public $userCity; public $userState; public function mount() { $this->selectedPostalCode = session('PostalCodeID', null); } #[On('postalCodeUpdated')] public function handlePostalCodeUpdate($data) { $this->userCity = $data['city']; $this->userState = $data['state']; // Auto-populate address fields $this->updateAddressFromPostalCode($data); } private function updateAddressFromPostalCode($postalData) { // Handle postal code selection logic $this->dispatch('addressUpdated', $postalData); } }
- In your Blade template:
<livewire:postal-code-select-flux wire:model="selectedPostalCode" :selected-postal-code-changed="$selectedPostalCode" display-format="city_state_zip" />
Configuration
1. Database Setup
Ensure your postal_codes
table has the structure:
// Migration example Schema::create('postal_codes', function (Blueprint $table) { $table->id(); $table->integer('PostalCodeID')->unique(); $table->string('PostalCode', 10); // e.g., '80202', '80202-1234' $table->string('City', 100); $table->string('State', 2); // State abbreviation $table->string('County', 100)->nullable(); $table->integer('CountryID')->default(1); $table->timestamps(); $table->index(['State', 'City']); $table->index('PostalCode'); });
Sample Data:
// Seeder example PostalCode::create([ 'PostalCodeID' => 1, 'PostalCode' => '80202', 'City' => 'Denver', 'State' => 'CO', 'County' => 'Denver County', 'CountryID' => 1 ]); PostalCode::create([ 'PostalCodeID' => 2, 'PostalCode' => '80203', 'City' => 'Denver', 'State' => 'CO', 'County' => 'Denver County', 'CountryID' => 1 ]);
2. Component Parameters
<livewire:postal-code-select-flux wire:model="selectedValue" {{-- Two-way data binding --}} :selected-postal-code-changed="$currentValue" {{-- Initial value --}} :placeholder="'Choose your location'" {{-- Custom placeholder --}} :display-format="'city_state_zip'" {{-- Display format --}} :filters="['State' => 'CO']" {{-- Geographic filters --}} :attrs="['class' => 'location-select']" {{-- Additional HTML attributes --}} />
3. Display Formats
Available formats:
full
: "Denver, CO 80202 (Denver County)"city_state_zip
: "Denver, CO 80202" (default)city_zip
: "Denver 80202"custom
: Use custom formatting method
{{-- Full format with county --}} <livewire:postal-code-select-flux display-format="full" /> {{-- Compact format --}} <livewire:postal-code-select-flux display-format="city_zip" /> {{-- Standard format --}} <livewire:postal-code-select-flux display-format="city_state_zip" />
4. Geographic Filtering
Filter by State:
<livewire:postal-code-select-flux :filters="['State' => 'CO']" placeholder="Select Colorado location" />
Filter by Multiple Criteria:
<livewire:postal-code-select-flux :filters="[ 'State' => 'CO', 'County' => 'Denver County' ]" />
Dynamic Filtering in Parent Component:
public $selectedState = 'CO'; public $availablePostalCodes = []; public function updatedSelectedState($state) { // Re-render postal code component with new filter $this->dispatch('updatePostalCodeFilter', ['State' => $state]); }
5. Usage Examples
Basic Postal Code Selection:
<livewire:postal-code-select-flux />
Service Area Selection:
<div class="service-area-form"> <label>Select Service Area:</label> <livewire:postal-code-select-flux wire:model="serviceArea" :filters="['State' => 'CO', 'County' => 'Denver County']" display-format="city_state_zip" placeholder="Choose service area" /> </div>
Address Form Integration:
class AddressFormComponent extends Component { public $selectedPostalCode; public $address = [ 'city' => '', 'state' => '', 'postal_code' => '', 'county' => '' ]; #[On('postalCodeUpdated')] public function handlePostalCodeSelection($data) { $this->address = [ 'city' => $data['city'], 'state' => $data['state'], 'postal_code' => $data['postalCode'], 'county' => $data['county'] ]; // Auto-populate form fields $this->dispatch('addressFieldsUpdated', $this->address); } }
<form> <div class="form-group"> <label>Location:</label> <livewire:postal-code-select-flux wire:model="selectedPostalCode" :selected-postal-code-changed="$selectedPostalCode" /> </div> {{-- Auto-populated fields --}} <input type="text" value="{{ $address['city'] }}" readonly> <input type="text" value="{{ $address['state'] }}" readonly> <input type="text" value="{{ $address['postal_code'] }}" readonly> </form>
6. Event Handling
The component dispatches comprehensive postalCodeUpdated
events:
#[On('postalCodeUpdated')] public function handlePostalCodeUpdate($data) { // $data contains: // - 'id': Database ID // - 'postalCodeId': PostalCodeID field // - 'city': City name // - 'state': State abbreviation // - 'postalCode': Postal code // - 'county': County name // - 'countryId': Country ID // - 'displayText': Formatted display string $this->updateLocationPreferences($data); $this->loadNearbyServices($data['postalCode']); $this->calculateShippingCosts($data); }
7. Session Integration
- Session key:
PostalCodeID
- Persistent selection: User's postal code choice persists across sessions
- Auto-restoration: Component automatically loads saved selection on mount
// Manual session management public function mount() { $this->selectedPostalCode = session('PostalCodeID', null); } public function updatedSelectedPostalCode($value) { session(['PostalCodeID' => $value]); }
8. Advanced Customization
Custom Display Format: Extend the component for custom formatting:
class CustomPostalCodeSelectFlux extends PostalCodeSelectFlux { protected function customFormatDisplay(PostalCode $postalCode): string { return "{$postalCode->City} ({$postalCode->PostalCode}) - {$postalCode->County}"; } }
Search Integration:
public function searchPostalCodes($searchTerm) { $this->filterOptions($searchTerm); $this->render(); // Re-render with filtered options }
9. Performance Optimization
Lazy Loading:
{{-- Load postal codes only when needed --}} <livewire:postal-code-select-flux lazy />
Pagination for Large Datasets:
// In a custom extended component protected function loadPostalCodes(): void { $this->options = PostalCode::select(/* fields */) ->limit(500) // Limit initial load ->get(); }
10. Troubleshooting
No postal codes appearing:
- Verify postal_codes table has data
- Check database connection
- Ensure proper column names match model
- Verify filters aren't too restrictive
Performance issues:
- Add database indexes on State, City, PostalCode
- Consider lazy loading for large datasets
- Implement search functionality for better UX
Session not persisting:
- Verify Laravel session configuration
- Check session driver and middleware
- Ensure session storage is writable
Incorrect location data:
- Verify postal code data accuracy in database
- Check PostalCode model fillable fields
- Ensure proper data seeding
Contributing
Please see contributing.md for details and a todolist.
Security
If you discover any security related issues, please email author email instead of using the issue tracker.
Credits
License
license. Please see the license file for more information.