Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Xml Wrangler Laravel Package

saloonphp/xml-wrangler

XML helper for Saloon: parse XML responses into arrays/objects, map nodes to data, handle namespaces, attributes and CDATA, and build or transform XML payloads cleanly. Great for SOAP-style APIs and legacy XML integrations in Laravel/PHP.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require saloonphp/xml-wrangler
    
  2. First Use Case: Parsing XML Responses Parse XML from a Saloon HTTP response:

    use SaloonHttp\Connectors\Connector;
    use SaloonPhp\XmlWrangler\XmlReader;
    
    class ApiConnector extends Connector
    {
        public function resolve(): array
        {
            $xml = XmlReader::fromResponse($this->response);
            return $xml->query('//data/item')->map(fn($el) => [
                'id' => $el->getAttribute('id'),
                'name' => $el->getContent(),
            ])->all();
        }
    }
    
  3. First Use Case: Generating XML Requests Create XML payloads for API requests:

    use SaloonPhp\XmlWrangler\XmlWriter;
    
    $writer = XmlWriter::new()
        ->element('request', fn($w) => $w
            ->attribute('version', '1.0')
            ->element('user', fn($w) => $w
                ->attribute('id', '123')
                ->element('name', 'John Doe')
            )
        );
    
    $this->request->withBody($writer->toXmlString());
    
  4. Where to Look First:

    • Core Classes: XmlReader (parsing) and XmlWriter (generation).
    • Saloon Integration: Saloon Documentation for HTTP client usage.
    • Examples: GitHub README for quickstart patterns.
    • Type Safety: Use generics in Query for DTOs (PHP 8.1+).

Implementation Patterns

1. Saloon HTTP Response Parsing

Parse XML responses in Saloon connectors with type safety:

use SaloonHttp\Connectors\Connector;
use SaloonPhp\XmlWrangler\Query;

class PaymentGatewayConnector extends Connector
{
    public function resolve(): array
    {
        $xml = XmlReader::fromResponse($this->response);
        return Query::fromXml($xml->query('//payments/payment'))
            ->map(fn($el) => [
                'transaction_id' => $el->getAttribute('id'),
                'amount' => (float) $el->getContent('amount'),
                'status' => $el->getContent('status'),
            ])
            ->all();
    }
}

2. Dynamic XML Requests from Eloquent

Generate XML requests from Laravel models:

use SaloonPhp\XmlWrangler\XmlWriter;
use App\Models\User;

$user = User::find(1);

$writer = XmlWriter::new()
    ->element('user_update', fn($w) => $w
        ->attribute('id', $user->id)
        ->element('email', $user->email)
        ->element('status', $user->status)
    );

$this->request->withBody($writer->toXmlString());

3. Handling Namespaced XML (SOAP)

Work with namespaced XML (e.g., SOAP APIs):

$reader = XmlReader::fromResponse($this->response)
    ->withNamespaces([
        'soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
        'ns'   => 'http://example.com/ns',
    ]);

$response = $reader->query('//soap:Envelope/soap:Body/ns:response')->first();
$status = $response->getContent('status');

4. Type-Safe Parsing with DTOs

Use generics for type-safe XML parsing (PHP 8.1+):

use SaloonPhp\XmlWrangler\Query;

class PaymentDto {
    public function __construct(
        public string $transactionId,
        public float $amount,
        public string $status,
    ) {}
}

$payments = Query::fromXml<PaymentDto>($reader->query('//payments/payment'))
    ->map(fn($el) => new PaymentDto(
        transactionId: $el->getAttribute('id'),
        amount: (float) $el->getContent('amount'),
        status: $el->getContent('status'),
    ))
    ->all();

5. Streaming Large XML Responses

Process large XML responses incrementally:

$reader = XmlReader::fromResponse($this->response);
$reader->query('//records/record')->each(fn($el) => {
    $this->dispatch(new ProcessRecord($el->getContent('data')));
});

6. Laravel Service Container Binding

Bind XmlReader/XmlWriter for dependency injection:

// app/Providers/AppServiceProvider.php
$this->app->bind(XmlReader::class, function ($app) {
    return XmlReader::fromResponse($app['saloon']->response);
});

7. Testing XML Output

Use snapshot testing for XML generation:

it('generates correct XML', function () {
    $writer = XmlWriter::new()
        ->element('invoice', fn($w) => $w
            ->attribute('id', 'INV-123')
            ->element('total', 100.00)
        );

    expect($writer->toXmlString())->toMatchSnapshot();
});

8. Custom Query Helpers

Extend XmlReader for domain-specific logic:

class PaymentXmlReader extends XmlReader
{
    public function findByTransactionId(string $id): ?XmlElement
    {
        return $this->query("//payment[@transaction_id='$id']")->first();
    }
}

9. Transforming XML to Collections

Convert XML to Laravel Collections:

use Illuminate\Support\Collection;

$reader = XmlReader::fromResponse($this->response);
$collection = collect($reader->query('//items/item')->map(fn($el) => [
    'id' => $el->getAttribute('id'),
    'name' => $el->getContent('name'),
    'price' => (float) $el->getContent('price'),
])->all());

10. Writing XML to Files or Responses

Save XML output to files or return in HTTP responses:

// Save to file
file_put_contents('export.xml', $writer->toXmlString());

// Return in Saloon response
$this->response->withBody($writer->toXmlString());

11. Error Handling

Handle malformed XML gracefully:

try {
    $reader = XmlReader::fromResponse($this->response);
    // Parse logic
} catch (\SaloonPhp\XmlWrangler\Exceptions\XmlException $e) {
    $this->response->withStatus(400)->withBody('Invalid XML: ' . $e->getMessage());
}

12. Pretty Printing for Debugging

Enable pretty printing for readability:

$writer = XmlWriter::new()->prettyPrint();
$writer->element('root', fn($w) => $w
    ->element('item', 'Test')
);

Gotchas and Tips

Pitfalls

  1. Null Checks for first()/sole(): Always validate results to avoid NullPointerException:

    if ($element = $reader->query('//item')->first()) {
        $name = $element->getContent('name'); // Safe
    }
    
  2. XPath Syntax Errors:

    • Use //child for any-depth searches.
    • Escape special characters in attributes (e.g., @attr='value&amp;amp;').
    • Test XPath queries with tools like XPath Tester.
  3. Stream Positioning: Rewind streams before parsing:

    $reader = XmlReader::fromResponse($this->response)->rewind();
    
  4. PHP 8.4+ Nullable Types: Handle nullable returns explicitly:

    $content = $el->getContent() ?? 'default';
    
  5. Namespace Conflicts: Ensure namespace prefixes match the XML document:

    $reader->withNamespaces(['ns' => 'http://example.com/ns']);
    
  6. Case Sensitivity in XPath: XPath is case-sensitive by default. Use local-name() for case-insensitive queries:

    $reader->query("//*[local-name()='Item']");
    
  7. Memory Issues with Large XML: Use each() for lazy evaluation instead of all():

    $reader->query('//records')->each(fn($el) => {
        // Process one record at a time
    });
    
  8. Saloon Response Body Handling: Ensure the response body is a string before parsing:

    if (!$this->response->isSuccessful() || !$this->response->hasBody()) {
        throw new \RuntimeException('Invalid response');
    }
    $xml = XmlReader::load((string) $this
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport