mtownsend/collection-xml
mtownsend/collection-xml adds a simple toXml() macro to Laravel collections and arrays, making it easy to convert nested data into clean XML for feeds, APIs, exports, and legacy integrations.
composer require mtownsend/collection-xmlMtownsend\CollectionXml\CollectionXmlServiceProvider::class in config/app.php.vendor/mtownsend/collection-xml/src. The README is short and the package is small — the service provider is where the macros are registered, and it's the fastest way to confirm the exact method signatures and options for the version you installed.php artisan tinker:collect(['user' => ['name' => 'Ada', 'role' => 'admin']])->toXml();
If you get an XML string back, you're wired up.
First real use case: exporting an Eloquent result set as an XML feed, or ingesting an XML webhook body into a collection so the rest of your app only deals with arrays/collections.
Export (collection → XML). The macro lives on every Collection instance. The day-to-day shape is query → map to plain arrays → toXml() → store or return.
$xml = User::query()->get()
->map(fn ($u) => ['id' => $u->id, 'email' => $u->email])
->toXml();
Storage::put('exports/users.xml', $xml);
Import (XML → collection). Parse once at the boundary — controller, job, or a small XmlPayload DTO — then pass the collection everywhere else. Don't leak raw XML strings into your domain layer.
$payload = Collection::fromXml($request->getContent());
$orders = $payload->get('orders.order', collect());
Round-tripping. Because both directions yield collections, normalization is just fromXml($in)->map(...)->toXml(). This is the sweet spot: reshaping a third-party XML format into your own.
Wrap it in a service class. Put root element name, declaration, and array shaping in one app-level class. With a single-maintainer, 44-star package, this is your insurance policy — swapping implementations later becomes a one-file change.
HTTP layer pairing. Combine with mtownsend/request-xml (XML request body → array) and mtownsend/response-xml (XML responses) so controllers stay thin and collection-xml only handles the array↔XML hop.
Testing. Assert on parsed XML, not strings: simplexml_load_string($xml) and check nodes. String assertions break on whitespace and attribute ordering.
['item' => [1,2,3]] produces three <item> elements — but fromXml can't tell a one-element list from a scalar. Normalize explicitly after parsing.&, or : yield invalid or namespaced XML. Sanitize or map keys before calling toXml().<user id="1"> or XSD-valid output, build it with DOMDocument/XMLWriter instead.null and [] (self-closing vs. omitted) with a test before shipping to anything schema-validated.dd() the raw string first — it's almost always an upstream value containing markup, not the package.mb_*). Emoji and non-ASCII are the usual source of mojibake or parse failures.toXml() builds the entire string in memory. For big exports, chunk() and append, or stream with XMLWriter.xmllint --format - locally; a false from simplexml_load_string() plus libxml_get_errors() pinpoints the bad node instantly.->toSitemapXml()), and check php artisan vendor:publish after install for a config file where defaults like root name live.How can I help you explore Laravel packages today?