A comprehensive PHP package for interacting with the Lexware Office API, built on top of the powerful Saloon HTTP client. This package provides a clean, object-oriented interface for managing articles, contacts, invoices, and other entities in Lexware Office.
- π Modern Architecture: Built with PHP 8.1+ features and best practices
- π§ Extensible Design: Easy to add new API endpoints and entities
- π Comprehensive Data Handling: Type-safe data objects with validation
- π Automatic Pagination: Built-in support for paginated API responses
- β‘ Rate Limiting: Automatic rate limiting to comply with API limits
- π§ͺ Testing Support: Comprehensive testing utilities and mock responses
- π Rich Documentation: Extensive PHPDoc and inline documentation
- π‘οΈ Error Handling: Centralized exception handling with detailed error information
You can install the package via composer:
composer require nodus-it/lexware-office-apiYou can publish the config file with:
php artisan vendor:publish --tag="lexware-office"Set your API token in your .env file (available at https://app.lexoffice.de/addons/public-api):
LEXWARE_OFFICE_API_TOKEN=your-token-hereThe package provides the following configuration options:
# Authentication
LEXWARE_OFFICE_API_TOKEN=your-token-here
# API timeouts
LEXWARE_OFFICE_CONNECT_TIMEOUT=30
LEXWARE_OFFICE_REQUEST_TIMEOUT=30
# Rate limiting
# Cache store used by the rate limiter (Laravel cache store name)
LEXWARE_OFFICE_RATE_LIMIT_STORE=fileuse Nodus\LexwareOfficeApi\Utils\LexwareOfficeConnector;
use Nodus\LexwareOfficeApi\Resources\ArticleResource;
// Create connector
$connector = new LexwareOfficeConnector();
// Create resource
$articles = new ArticleResource($connector);
// Get all articles
$allArticles = $articles->all();
// Get specific article
$article = $articles->get('article-id');
// Create new article
$newArticle = $articles->create(ArticleData::from([
'title' => 'New Product',
'articleNumber' => 'PROD-001',
'description' => 'Product description',
'unitName' => 'StΓΌck',
'price' => [
'currency' => 'EUR',
'netAmount' => 19.99,
'grossAmount' => 23.79,
'taxRatePercentage' => 19.0,
],
]));use Nodus\LexwareOfficeApi\Utils\ResourceFactory;
use Nodus\LexwareOfficeApi\Utils\LexwareOfficeConnector;
$connector = new LexwareOfficeConnector();
// Create resources using factory
$articles = ResourceFactory::create('articles', $connector);
$contacts = ResourceFactory::create('contacts', $connector);
$invoices = ResourceFactory::create('invoices', $connector);use Nodus\LexwareOfficeApi\Data\Enums\ArticleType;
// Filter articles by type
$serviceArticles = $articles->findByType(ArticleType::SERVICE);
// Filter by article number
$specificArticles = $articles->findByArticleNumber('PROD-001');
// Filter by GTIN
$gtinArticles = $articles->findByGtin('1234567890123');
// Complex filtering
$filteredArticles = $articles->all(
filterType: ArticleType::PRODUCT,
filterArticleNumber: 'PROD-*'
);use Nodus\LexwareOfficeApi\Exceptions\LexwareOfficeException;
try {
$article = $articles->get('non-existent-id');
} catch (LexwareOfficeException $e) {
if ($e->isValidationError()) {
$errors = $e->getValidationErrors();
// Handle validation errors
} elseif ($e->isClientError()) {
// Handle client errors (4xx)
} elseif ($e->isServerError()) {
// Handle server errors (5xx)
}
echo "Error: " . $e->getMessage();
echo "Status: " . $e->getStatusCode();
}// Iterate through all pages
foreach ($articles->all() as $article) {
echo $article->title . "\n";
}
// Manual pagination
$paginator = $articles->all();
$firstPage = $paginator->items(); // Get items from first page
if ($paginator->hasNextPage()) {
$nextPage = $paginator->nextPage();
}The package includes comprehensive testing utilities:
use Nodus\LexwareOfficeApi\Tests\LexwareOfficeTestCase;
class ArticleTest extends LexwareOfficeTestCase
{
public function test_can_get_article()
{
// Mock successful response
$this->mockClient->addResponse(
$this->mockSuccessResponse($this->getSampleArticleData())
);
$articles = new ArticleResource($this->connector);
$article = $articles->get('article-123');
$this->assertEquals('Sample Article', $article->title);
$this->assertRequestSent('GET', 'articles/article-123');
}
public function test_handles_validation_errors()
{
$this->mockClient->addResponse(
$this->mockValidationErrorResponse([
'title' => ['Title is required']
])
);
$this->expectException(LexwareOfficeException::class);
$articles = new ArticleResource($this->connector);
$articles->create(ArticleData::from([]));
}
}- BaseResource: Template pattern for all API resources
- BaseRequest: Abstract base for all API requests with common functionality
- BaseData: Enhanced data objects with validation and transformation
- LexwareOfficeException: Centralized error handling
- BaseGetRequest: For retrieving single entities
- BaseListRequest: For retrieving collections with pagination
- BaseCreateRequest: For creating new entities
- BaseUpdateRequest: For updating existing entities
- BaseDeleteRequest: For deleting entities
- HasTimestamps: Adds created/updated timestamp handling
- HasVersioning: Adds version control for optimistic locking
- Create a new resource class extending
BaseResource:
class ContactResource extends BaseResource
{
protected function getEndpoint(): string
{
return 'contacts';
}
protected function getRequestNamespace(): string
{
return 'Nodus\\LexwareOfficeApi\\Requests\\Contacts';
}
protected function getDataClass(): string
{
return ContactData::class;
}
}- Register the resource in the factory:
ResourceFactory::register('contacts', ContactResource::class);class GetContactRequest extends BaseGetRequest
{
protected function resolveEndpoint(): string
{
return "contacts/{$this->id}";
}
}class ContactData extends BaseData
{
use HasTimestamps;
use HasVersioning;
public string $id;
public ?string $organizationName;
public ?PersonData $person;
public ?AddressData $addresses;
}Currently implemented:
- β Articles (complete)
Planned:
- π Contacts
- π Invoices
- π Vouchers
- π Quotations
- π Credit Notes
- π Order Confirmations
- π Delivery Notes
- π Recurring Templates
- π Payment Conditions
- π Countries
- π Files
- π Profile
- π Event Subscriptions
This package uses GitHub Actions for continuous integration and testing:
- Multi-PHP Version Testing: Tests run on PHP 8.1, 8.2, and 8.3
- Dependency Matrix: Tests with both
prefer-lowestandprefer-stabledependencies - Code Coverage: Comprehensive coverage reporting with Codecov integration
- Code Quality: Automated checks with PHPCS, PHPStan, and PHPMD
- Security Audits: Regular dependency vulnerability scanning
# Install dependencies
composer install
# Run tests
vendor/bin/phpunit
# Run tests with coverage
vendor/bin/phpunit --coverage-html coverage
# Run code quality checks (if tools are installed)
vendor/bin/phpcs --standard=PSR12 src/ tests/
vendor/bin/phpstan analyse src/ --level=5
vendor/bin/phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcodeThe project includes several GitHub Actions workflows:
- Tests: Runs on every push and pull request
- Coverage: Generates and uploads coverage reports
- Security: Weekly dependency security audits
- Release: Automated releases for tagged versions
Contributions are welcome! Please feel free to submit a Pull Request.
- Follow PSR-12 coding standards
- Write comprehensive tests for new features
- Update documentation for API changes
- Ensure all CI checks pass before submitting PR
The MIT License (MIT). Please see License File for more information.