Distribute content evenly in tables with php

Asked

Viewed 1,002 times

4

I set up a php algorithm for popular 4 html tables in a uniform way.

However, I am finding my code somewhat dirty and would like to check if there is a cleaner way to create this code.

What it does: It takes an array with names and extension numbers, and distributes evenly across 4 tables, no matter how many numbers and extensions, if it’s a number not divisible by 4, it takes the rest and distributes.

Detail: The array is just a test, the result comes from a CRUD, which has a BD.

The use of inline HTML properties is only test, in the system will be managed by the responsible CSS classes.

Then I ask you to direct your attention exclusively to the result-distributing algorithm.

Follow my array:

<?php
$grupos = array(
    'adm' => array(
        0 => array(
            'nome' => 'nome0',
            'ramal' => 'ramal0'
        ),
        1 => array(
            'nome' => 'nome1',
            'ramal' => 'ramal1'
        ),
        2 => array(
            'nome' => 'nome2',
            'ramal' => 'ramal2'
        ),
        3 => array(
            'nome' => 'nome3',
            'ramal' => 'ramal3'
        ),
        4 => array(
            'nome' => 'nome4',
            'ramal' => 'ramal4'
        ),
        5 => array(
            'nome' => 'nome5',
            'ramal' => 'ramal5'
        ),
        6 => array(
            'nome' => 'nome6',
            'ramal' => 'ramal6'
        ),
        7 => array(
            'nome' => 'nome7',
            'ramal' => 'ramal7'
        ),
        8 => array(
            'nome' => 'nome8',
            'ramal' => 'ramal8'
        ),
        9 => array(
            'nome' => 'nome9',
            'ramal' => 'ramal9'
        )
    )
);

Code

define("DIVISOR", 4); //Número de tabelas
$array_size = count($grupos['adm']); //quantidade de ramais
$qtd_por_tabela = (int) ($array_size / DIVISOR); //quantidade em cada tabela
$resto = ($array_size % DIVISOR); //resto a ser dividido nas tabelas
$y = 0; //contador array

for ($i = 1; $i <= DIVISOR; $i++) {
    echo "<div class=\"tables\"><table>";
    $sobra = $resto > 0 ? 1 : 0;
    for ($x = 1; ($x <= ($qtd_por_tabela + $sobra)); $x++) {
        $tag = "<tr><td>";
        $tag.=$grupos['adm'][$y]['nome'];
        $tag.="</td><td>";
        $tag.= $grupos['adm'][$y]['ramal'];
        $tag.="</td></tr>";
        echo $tag;
        $y++;
    }
    $resto = $resto > 0 ? ($resto - 1) : 0;
    echo "</table></div>";
    echo "\r\n";
}
?>

A bit of CSS to play one table next to another...

<style>
    table,
    table td{
    border: 1px solid #000;
    }
    .tables{
        float: left;
        margin: 10px;
    }
</style>

The code prints the following result:

<div class="tables">
    <table>
        <tr>
            <td>nome0</td>
            <td>ramal0</td>
        </tr>
        <tr>
            <td>nome1</td>
            <td>ramal1</td>
        </tr>
        <tr>
            <td>nome2</td>
            <td>ramal2</td>
        </tr>
    </table>
</div>
<div class="tables">
    <table>
        <tr>
            <td>nome3</td>
            <td>ramal3</td>
        </tr>
        <tr>
            <td>nome4</td>
            <td>ramal4</td>
        </tr>
        <tr>
            <td>nome5</td>
            <td>ramal5</td>
        </tr>
    </table>
</div>
<div class="tables">
    <table>
        <tr>
            <td>nome6</td>
            <td>ramal6</td>
        </tr>
        <tr>
            <td>nome7</td>
            <td>ramal7</td>
        </tr>
    </table>
</div>
<div class="tables">
    <table>
        <tr>
            <td>nome8</td>
            <td>ramal8</td>
        </tr>
        <tr>
            <td>nome9</td>
            <td>ramal9</td>
        </tr>
    </table>
</div>

Note: It does not intricate so cute, but I did manually to facilitate reading. The idea is to achieve this result, but with a more "clean algorithm".

Someone knows how to do?

  • 1

    a suggestion I would make: take the strings with html codes from for and popular the table directly in html, via same server or via Ajax with JSON

  • basically your code gets polluted by the injection of data into the logical part (something very bad in the concept of programming), what you need to do is remove this (as in the previous comment, popular in a JSON file or database), if you want to continue I advise you to leave the result directly in HTML to minimize, without this I see no other way =/

  • in JSON minimizes the impacts and we can easily assist in this part (it will still be compatible with the helper for cakephp, because we will use php to load the file);

  • Guys, this array is not created by me, it comes from a database, the array is just a test, to make it easier to understand the format the data is received. I want ideas for the algorithm, the rest is just a testing structure, without relying on BD.

  • So you can easily simulate the structure...

  • Just one detail: echo '<div class="tables"><table border="1px" bordercolor="#000000">'; or echo "<div class='tables'><table border='$variavelPHP' bordercolor='#000000'>";

  • Guys, I clarified that css is just for testing. Don’t get too caught up in the detail, I want to improve the php algorithm;

  • 3

    @Marceloaymone, follow the discussion about the reopening here, and give your opinion if you think it is pertinent: http://meta.pt.stackoverflow.com/questions/1903

  • What happens, the moment I asked the question, I simplified the problem so I could have help more easily. As for breaking in layers, this was done, but I did not demonstrate here because I use a framework, and in this way I abstract the problem to focus on the algorithm.

Show 4 more comments

1 answer

6


There is a Structural Design Pattern called Composite which allows you to abstract HTML and build a hierarchical structure of composite information, such as a tree.

With Composite you have several objects, each representing a branch of HTML, these branches may or may not be composed of other elements.

So the answer doesn’t get bigger than it already will, I won’t post the codes here but all together in this Gist. Also included is a very simple implementation of a autoloader to run the Application and a small visual appeal with CSS.

First, the directory structure as well as the files used:

|-index.php
|-Composite\Components
|   |-Composite\Components\AbstractComponent.php
|   |-Composite\Components\Component.php
|   |-Composite\Components\Drawable.php
|   \-Composite\Components\HTML
|     |-Composite\Components\HTML\Cell.php
|     |-Composite\Components\HTML\Row.php
|     \-Composite\Components\HTML\Table.php

"Let’s meet now our participants":

  • Abstractcomponent.php

This abstract superclass defines an interface for all objects that will compose some element, some node of the hierarchical structure, and implements a standard behavior for them.

The estate Abstractcomponent: stores all child nodes of each created element, provided that its condition of existence is respected, allowing the rendering at structure levels.

This "condition of existence" is controlled by property Abstractcomponent::Leaf that determines whether a given object is a branch (FALSE) or a sheet (TRUE). Branches can be composed of other child elements, whereas "leaves", no.

In this particular scenario, in order to simplify, I will be considering table cells as sheets, even if in a real case they are not.

The method Abstractcomponent::add() is what effectively allows Composition to occur in order to aggregate new objects to each other.

Note that there is a check so that objects defined as being a "sheet" do not accept new objects, triggering a logical exception.

Last but not least, AbstractComponent::drawChildren() offers to the objects representing the branches a means of rendering all the elements contained in them. As you can see below, "sheet" objects are the only ones that do not use it.

  • Component and Drawable

We have two interfaces, one to represent the components and the other to represent render objects. In fact only the interface Component could be used, but, by preference, I divided the behaviors into two interfaces making Component a guy.

  • Table, Row and Cell

For this example there are objects to represent only three HTML elements, however, you can create others as you need, simply extend the superclass Abstractcomponent and implement the method Drawable::draw(). And of course, if it is necessary to characterize an element as "sheet", overwrite the previously described protected property.

So much Table as Row are quite simple, just involve the child-knots rendered through AbstractComponent::drawChildren() by tags <table> / </table> and <tr> / </tr>, respectively.

Cell, however, it is slightly different. Besides the fact that, here exclusively, we have reversed its characteristic of not allowing other levels in Composition, we overwrite the constructor to have a way to define the text that will be written in the cell.

Normally at that time would come that gigantic warning on a neon sign saying that when overriding the constructor of a superclass, one should reinject it through Rent::__Construct(). However, this is not at all mandatory in this Application because, since the flag Abstractcomponent::Leaf has been superscripted, Abstractcomponent::add() will trigger the logical exception if invoked in the context of an object Cell.

If we do not invoke the constructor, do not define that the object that overwritten the superclass is a sheet and still invoke Abstractcomponent::add() (Boy, we could miss three times :o), we’d get one Fatal Error for the method append() would be invoked without an object. Object that, a Arrayobject, defined precisely in the superclass or superscript constructor.

Just in case, just in case, let’s leave him there. It doesn’t hurt ^_^

Equally to Table and Row, Cell::draw() is very simple, just insert the value received by the constructor, now stored in a property with visibility private (because private is ugly :p) between a couple of tags <td> / </td>

But... and how does that apply to the problem?

First let’s change your input array to something a little bit different just to demonstrate that it works:

$groups = array(
    'Administração' => array(
        0 => array(
            'nome' => 'nome0',
            'ramal' => 'ramal0'
        ),
        1 => array(
            'nome' => 'nome1',
            'ramal' => 'ramal1'
        ),
        2 => array(
            'nome' => 'nome2',
            'ramal' => 'ramal2'
        ),
        3 => array(
            'nome' => 'nome3',
            'ramal' => 'ramal3'
        )
    ),
    'Financeiro' => array(
        4 => array(
            'nome' => 'nome4',
            'ramal' => 'ramal4'
        ),
        5 => array(
            'nome' => 'nome5',
            'ramal' => 'ramal5'
        ),
        6 => array(
            'nome' => 'nome6',
            'ramal' => 'ramal6'
        ),
        7 => array(
            'nome' => 'nome7',
            'ramal' => 'ramal7'
        ),
        8 => array(
            'nome' => 'nome8',
            'ramal' => 'ramal8'
        ),
        9 => array(
            'nome' => 'nome9',
            'ramal' => 'ramal9'
        )
    )
);

I just broke it to consider more than one department. Before iterating to compose our structure, we have to define the main element of it, that is, the table where each row and each cell will be aggregated:

$tableGroup = new Composite\Components\HTML\Table;

Now yes, let’s iterate:

foreach( $groups as $group => $persons ) {

    // Groups

    $groupRow = new Composite\Components\HTML\Row;

    $groupRow -> add( new Composite\Components\HTML\Cell( $group ) );

    $tableGroup -> add( $groupRow );

    // Persons

    foreach( $persons as $person ) {

        $nameCell = new Composite\Components\HTML\Cell( $person['nome'] );
        $dialCell = new Composite\Components\HTML\Cell( $person['ramal'] );

        $personRow = new Composite\Components\HTML\Row;

        $personRow -> add( $nameCell ) -> add( $dialCell );

        $tableGroup -> add( $personRow );
    }
}

First we create a line for the Department. We create an object Row and add an object to it Cell with the text due. Next we add this first composition to the previously created table.

Now let’s iterate the list of names and phones. See that this time I did it in a different way. I first created the two cells and assigned them the appropriate values. Only then did I create the line and perform the Composition.

Nothing would stop me, for example, from doing so:

$personRow = new Composite\Components\HTML\Row;

$personRow -> add( new Composite\Components\HTML\Cell( $person['nome'] ) )
           -> add( new Composite\Components\HTML\Cell( $person['ramal'] ) );

However, this way you improve the flow and prevent the cells from receiving additional props (see below).

Before closing the inner loop, we add the Composition of the row to the table.

Now it remains to render everything. Outside of the loop we invoke the method Drawable::draw() in the context of Table and the AbstractComponent::drawChildren() it used will render everything at once.

If you want individual tables, one for each department, it is enough that the table created in $tableGroup be done right at the beginning of the first loop and, its rendering, right at the end.

Another point to consider is the fact that the line reserved for the Department name does not extend over the entire width of the table.

This was both deliberate and accidental. Purpose to simplify implementation by disregarding any attributes for the elements, as in the case, the colspan in the first line cell.

And accidental because at the time I wrote this code, I couldn’t remember how to merge cells >.<

But this kind of enhancement is now up to you. You can, for example, create a second argument in the Cell, with an array of attributes that you would insert into the TD declaration.

And since implode() does not work with associative arrays, it may not seem, but http_build_query() is fantastic for this.

Despite everything, it is worth noting that, today, with the existing template tools on the market, this type of implementation of Composite is not very useful, after all, it is much easier for you to open and close some PHP tags and write HTML directly in a template, that can even be curly, than opt for this approach.

  • Although the files in Gist have allowed me to include the directory separator bars as soon as possible I edit the answer to put one sketch improved.

  • What happens, the moment I asked the question, I simplified the problem so I could have help more easily. As for breaking in layers, this was done, but I did not demonstrate here because I use a framework, and thus abstract the problem to focus on the algorithm.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.