Weave 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

Component Text Matrix Laravel Package

serendipity_hq/component-text-matrix

A PHP component for building and manipulating text matrices, with helpers for rows, columns, padding, alignment, and rendering. It integrates cleanly with Laravel and other PHP projects, making tabular or grid-based text output easy to format.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer and confirm the PHP constraint first, since the package tracks modern PHP versions:

composer require serendipity_hq/component-text-matrix
// composer.json of the library — check require.php before adopting
"require": { "php": "^8.2" }

The documentation is thin, so the fastest path to a working mental model is to open vendor/serendipity_hq/component-text-matrix/src/ and read the main class (typically TextMatrix). It is a small, single-responsibility library: you hand it a two-dimensional array, it hands you back a string. There is no framework coupling — the repo's Docker/Make/Symfony tooling in AGENTS.md is for developing the library, not for consuming it.

A first end-to-end use case — dumping rows you already have (a query result, a comparison matrix, a CLI report) as an ASCII table:

require __DIR__ . '/vendor/autoload.php';

use SerendipityHQ\Component\TextMatrix\TextMatrix;

$matrix = [
    ['id', 'name',  'role'],
    [1,    'Alice', 'admin'],
    [2,    'Bob',   'editor'],
    [3,    'Carol', 'viewer'],
];

$textMatrix = new TextMatrix($matrix);
echo $textMatrix->render();

Run it and inspect the output before wiring it anywhere else. The shape of the borders, padding and header treatment is the contract you are adopting, and it is easier to confirm visually than from the README. Once it looks right, drop it into an Artisan command ($this->line($textMatrix->render())) or a Symfony Console command ($output->writeln(...)).

Implementation Patterns

Header as the first row. The simplest integration passes the header row as element zero of the matrix. If the class exposes a dedicated header setter, prefer it — it usually also changes the header/body separator, which is what you want for reports but not for raw dumps.

Wrap it in a single formatter service. Do not instantiate TextMatrix at every call site. Create one small class in your app, e.g. App\Support\ConsoleTable, that owns the styling decisions:

final class ConsoleTable
{
    public function render(array $headers, array $rows): string
    {
        return (new TextMatrix([$headers, ...$rows]))->render();
    }
}

Every command then calls ConsoleTable and the whole application gets consistent tables for free. When you later switch to Symfony's Table or Laravel's $this->table(), you change one file.

Normalize before rendering. The renderer works on scalars. Flatten, cast and truncate upstream of the call:

$rows = array_map(static fn (array $row): array => array_map(
    static fn (mixed $cell): string => match (true) {
        $cell === null        => '—',
        is_bool($cell)        => $cell ? 'yes' : 'no',
        is_array($cell)       => implode(', ', $cell),
        default               => (string) $cell,
    },
    $row
), $rows);

This is the pattern that prevents 90% of the surprises described in the next section.

Stream rows for large datasets. If the API offers an incremental addRow()/addRows(), build the table progressively rather than materializing one giant array, and flush the rendered string per page of ~50 rows. For a few hundred rows the eager array is fine.

Snapshot-test the output. Rendered tables are deterministic strings, which makes them ideal for assertions:

self::assertStringContainsString('| Alice |', $this->renderTable());

Assert on substrings rather than the full block so padding tweaks in the library don't fail your suite.

Use it where plain text is the destination. Log files, CLI output, plain-text emails, PR comments, changelog snippets and diffable golden files. If the destination is HTML or a terminal that already has a table helper, use that instead.

Gotchas and Tips

Multibyte width is the classic failure. Column widths are computed from string length. If cells contain accented characters, CJK text or emoji, strlen-based padding will misalign. Test with your real data early — one à is usually harmless, a full-width character is not.

ANSI escape codes break alignment. If you colorize cell values before passing them in, the invisible escape sequences count toward the measured width and the columns shift. Apply color to the whole rendered block, or strip codes before measuring, or skip coloring for the table.

Ragged rows. Rows with differing column counts are a common cause of ugly output — either the library pads to the widest row or it truncates. Decide explicitly: pad every row to max(array_map('count', $rows)) with empty strings and you get predictable results regardless of behavior.

Newlines inside cells. A cell containing \n will split across visual rows and destroy the grid. Replace with a space or a ⏎ marker before rendering.

No magic wrapping. Long values are not auto-wrapped, so a single 400-character cell will produce an unreadable table. Truncate with mb_substr($value, 0, $max) and append an ellipsis, using mb_* functions throughout.

Array-to-string conversion errors. Passing an object without __toString or a nested array straight through triggers conversion warnings or a fatal error. Cast and flatten first (see the pattern above).

Watch the trailing newline. render() may or may not end with \n. If you concatenate several tables or embed one inside a larger block, rtrim() the result.

Pin the version and read the source. The library has a very small user base and no dependents, so the API surface is stable mostly by virtue of nobody changing it. There is no large community to lean on for answers. Lock a constraint in composer.json, and because the codebase is tiny, spend fifteen minutes reading the renderer class — you will learn more about edge-case behavior there than from any docs.

Fallbacks are one line away. If you need colspan, cell wrapping, HTML output or rich terminal styling, Symfony Console's Table or Laravel's $this->table() will get you further. Adopt TextMatrix for the simple cases and keep the seam (ConsoleTable above) so swapping is cheap.

Contributing back. The project is fully Dockerized and driven by make: make start 8.3 boots a specific PHP version, make sf 7.0 swaps the Symfony constraint via Flex. Per the repo's own rules, remove extra.symfony.require from composer.json before committing — it is local Flex state, not project configuration.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo