Suche Inspiration für HTML-Template / DOMDocument Parsing / Templateerweiterung

NTDY

Erfahrenes Mitglied
Hallo,

ich habe mal ein CMS geschrieben, welches HTML-Templates mittels DOMDocument() und der Methode loadHTML() so zerlegt, dass ich am Ende ein Multi-Array habe aus Elementen, Kindelementen und den entsprechenden Attributen.

Ziel ist, dass ich Templates durch Plugins dynamisch erweitern kann, wenn ich bspw. eine Lightbox für eine Bild schreibe.
Das Plugin erweitert darin das Multi-Array bspw. in der Form:

$aTemp['children']['0']['children']['0']['children']['0']['children']['0']['rel'] = 'lightbox';

Nun ist diese Lösung in soweit unschön, dass wenn sich die Struktur des Templates ändert, ich die Plugins ebenfalls ändern muss.

Also habe ich einen Analyzer geschrieben, der mir die Arrayposition eines Elements herausgibt:
$eRes = MyTools->findFirstElementInArray('a', $aTemp);
Der Rückgabewert von $eRes ist dann bspw.
['children']['0']['children']['0']['children']['0']['children']['0']

Da ich aber das Attribut "rel" mit dem Wert "lightbox" zurückgeben muss, behelfe ich mich mit der hässlichen Variante:

eval('$aTemp' . $eRes . '[\'rel\'] = \'lightbox\'');

Hat jemand von euch eine Idee wie man das eleganter lösen kann? Ich bin gerade inspirationslos.

Schöne Grüße
NTDY
 
Mir scheint, der Rückgabewert dieses Analyzers ist ein String? Dann fällt mir leider keine elegante Lösung ein. Nur diesen String zu zerlegen und die Schlüssel zu ermitteln aber das wird auch nicht besser.
Einfacher wäre es wenn der Analyzer ein Array mit den Schlüsseln zurück geben würde aber wahrscheinlich kannst Du das nicht mehr ohne weiteres ändern wenn anderswo häufig die Stringdarstellung verwendet wird.
 
PS: Ich bin der Sache mal weiter nach gegangen und das mit dem Zerlegen scheint mir doch keine so schlechte Idee zu sein:
Code:
$arr = [
    ['a' => ['rel' => 'rel0a'], 'b' => 2],
    ['c' => 2, 'd' => ['rel' => 'rel1d'],]
];

// die Funktion gibt eine Referenz auf ein
// Element im Array zurück:
function &getRef(
    // das Array wird als Referenz übergeben,
    // damit auch Werte darin geändert werden können:
    &$arr,
    // String mit den Schlüsseln:
    $keyStr
) {
    $result = preg_match_all('/[^\[\]\']+/', $keyStr, $matches);
    $ref = &$arr;
    foreach ($matches[0] as $key) {
        $ref = &$ref[$key];
    }
    return $ref;
}
var_dump(getRef($arr, "['0']['a']"));
var_dump(getRef($arr, "['0']['a']")['rel']);
getRef($arr, "['1']['d']")['rel'] = 'newval';
var_dump($arr);
 
Hallo Sempervivum,
vielen Dank für Deinen Code. Ich werde mal darüber sinnieren und mir Gedanken machen.
Nur zur Komplettierung meiner Beschreibung.

Mein Algorithmus zerlegt erst einmal mit DOMDocument() und der Methode loadHTML() HTML zu dieser Struktur

PHP:
Array
(
    [tag] => div
    [children] => Array
        (
            [0] => Array
                (
                    [tag] => div
                    [class] => inner
                    [html] =>
              
                    [children] => Array
                        (
                            [0] => Array
                                (
                                    [tag] => div
                                    [class] => picture
                                    [html] =>
    
                                    [children] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [tag] => figure
                                                    [html] =>
              
                                                    [children] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => a
                                                                    [href] => files/inhaltstypen/Cafe.png
                                                                    [title] => Café
                                                                    [html] =>
        
                    
                                                                    [children] => Array
                                                                        (
                                                                            [0] => Array
                                                                                (
                                                                                    [tag] => img
                                                                                    [alt] => Café
                                                                                    [title] => Café
                                                                                    [src] => /backend/files/inhaltstypen/Cafe.png
                                                                                    [width] => 300
                                                                                    [height] => 0
                                                                                )

                                                                        )

                                                                )

                                                            [1] => Array
                                                                (
                                                                    [tag] => figcaption
                                                                    [html] => Café
                                                                )

                                                        )

                                                )

                                        )

                                )

                            [1] => Array
                                (
                                    [tag] => div
                                    [class] => text
                                    [children] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [tag] => p
                                                    [html] => Der Inhaltstyp Text mit Bild ermöglicht die gleichzeitige Verwendung dieser Medien. Die Bilder können dabei unterschiedlich positioniert sein:
                                                )

                                            [1] => Array
                                                (
                                                    [tag] => ul
                                                    [html] =>

                                                    [children] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => li
                                                                    [html] => Bild links
                                                                )

                                                            [1] => Array
                                                                (
                                                                    [tag] => li
                                                                    [html] => Bild rechts
                                                                )

                                                            [2] => Array
                                                                (
                                                                    [tag] => li
                                                                    [html] => Bild links, Text umfließt das Bild
                                                                )

                                                            [3] => Array
                                                                (
                                                                    [tag] => li
                                                                    [html] => Bild rechts, Text umfließt das Bild
                                                                )

                                                            [4] => Array
                                                                (
                                                                    [tag] => li
                                                                    [html] => Bild oben
                                                                )

                                                        )

                                                )

                                            [2] => Array
                                                (
                                                    [tag] => p
                                                    [html] => Zur besseren Verdeutlichung wird im Folgenden Blindtext verwendet:
                                                )

                                            [3] => Array
                                                (
                                                    [tag] => p
                                                    [children] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => em
                                                                    [html] => Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.
                                                                )

                                                        )

                                                )

                                            [4] => Array
                                                (
                                                    [tag] => p
                                                    [children] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => em
                                                                    [html] => Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.
                                                                )

                                                        )

                                                )

                                        )

                                    [html] =>


                                )

                        )

                )

        )

)

Danach überge ich dieses Array an die Methode ->findFirstElementInArray('a', $aTemp); und der Algorithmus findet das erste <a>-Element und gibt als String die Position zurück.

PHP:
findFirstElementInArray($sSearchValue, $aArray)
        {
            $aArray = $this->findFirstTagName($sSearchValue, $aArray, $aPath = array());
            $sString = '';
            // -1 because the last element already refers to the tag element
            for ($i = 0; $i < count($aArray) - 1; $i++) {
                $sString .= '[\'' . $aArray[$i] . '\']';
            }
            return $sString;
        }

Wahrscheinlich muss ich hier in findFirstTagName ansetzen und das Ergebnis als Array ausgeben.
Wie gesagt: Ich werde mir Deinen Algorithmus mal anschauen bzw. du hast durch meinen Input eine weitere Idee :.)
 
Wahrscheinlich muss ich hier in findFirstTagName ansetzen und das Ergebnis als Array ausgeben.
Das wäre natürlich schöner und direkter als den Umweg über den String zu gehen.
Möglicher Weise könntest Du auch noch einen Schritt weiter gehen und gleich die Methode findFirstElementInArray('a', $aTemp) so anlegen, dass sie eine Referenz auf das betr. Element zurück gibt. Dann kannst Du wie in meinem Beispiel direkt auf das Element zugreifen und es auch ändern.
 
Hallo Sempervivum, vielen Dank für die Anregung. Die Methode habe ich nun umgebaut:

PHP:
        public function findFirstElementInArray($sSearchValue, $aArray)
        {
            $aArray = $this->findFirstTagName($sSearchValue, $aArray, $aPath = array());
            $aArrayReverse = array_reverse($aArray);
            $aTempArray = array();
            // Start with 1 because the last element already refers to the "tag" element
            for ($i = 1; $i < count($aArrayReverse); $i++) {
                if ($i == 1) {
                    $aTempArray = array($aArrayReverse[$i] => '');
                } else {
                    $aTempArray = array($aArrayReverse[$i] => $aTempArray);
                }
            }
            return $aTempArray;
        }

In den letzten Konversationen bin ich jedoch am Umlegen, ob ich die ganze Methode anders gestalten sollte. Ich soll künftig nicht mehr die Struktur des HTML kennen, sondern nur noch der Methode übergeben.
Bspw. in der Form:
  • In welchem Element soll etwas geändert werden?
  • Soll etwas ergänzt, ausgetauscht oder gelöscht werden
Aber ich muss da nochmal genau drüber nachdenken, welche Stolperfallen es da geben wird.
Danke erst einmal bis hierher.
NTDY
 
In dem Fall würde ich über meinen Vorschlag nachdenken, der Suchfunktion einfach einen Callback zu übergeben, darin kannst Du dann jeweils das, was zu tun ist, erledigen.

Eines verstehe ich noch nicht so richtig: Du schreibst da (wahrscheinlich beispielhaft) von einem rel-Attribut, das einem a-href-Element hinzu gefügt werden soll im Zusammenhang mit einer Lightbox. Das kenne ich so, dass es meistens mit mehreren Elementen gemacht wird?

Beim weiteren Überlegen hatte ich zunächst die Idee, dass man so etwas wie die CSS-Selektoren brauchen würde, wo man mit IDs, Klassen, Nachfahren und Geschwistern genau angeben kann, welche Elemente gemeint sind. Will man so etwas implementieren, wird es aber richtig kompliziert.

Als Alternative hatte ich dann die Idee, dass man
1. einen Filter-Callback und
2. einen Action-Callback
verwenden könnte.

Das könnte dann so aussehen:
Code:
$aArray = [
    [
        'tag' => 'div',
        'id' => 'gallery1',
        'children' => [
            ['tag' => 'a'],
            ['tag' => 'a'],
            ['tag' => 'a']
        ]
    ],
    [
        'tag' => 'div',
        'id' => 'gallery2',
        'children' => [
            ['tag' => 'a'],
            ['tag' => 'a'],
            ['tag' => 'a']
        ]
    ]
];
function modifyElements(&$arr, $filter, $action)
{
    foreach ($arr as &$ele) {
        if ($filter($ele)) {
            $action($ele);
        }
        if (isset($ele['children'])) {
            modifyElements($ele['children'], $filter, $action);
        }
    }
}
print_r($aArray);
// Beispiel für die Anwendung: Lightbox  wie ich sie kenne,
// ein Container mit a-href-Elementen darin:
modifyElements(
    $aArray,
    // Der Filter prüft ob ein Attribut "id"
    // mit dem Wert "gallery1" vorhanden ist:
    function (&$ele) {
        return (isset($ele['id']) && $ele['id'] == 'gallery1');
    },
    // Die Aktion besteht darin, allen Kindelementen
    // ein data-Attribut "data-lightbox"
    // mit dem Wert "gallery1" hinzu zu fügen:
    function (&$ele) {
        foreach ($ele['children'] as &$link) {
            $link['data-lightbox'] = 'gallery1';
        }
    }
);
print_r($aArray);
Die eigentliche Suchfunktion modifyElements wird dann sehr einfach.
Ich hoffe, ich habe die Struktur deines $aArray richtig verstanden.
 
Zuletzt bearbeitet:
Vielen Dank für Deine Inspiration Sempervivum. Das ist ein brauchbares gutes Beispiel.

P.S. Ändere noch den Funktionsaufruf von modifyElement auf modifyElements, damit es die nächsten Leser einfacher haben :)

Gruß
NTDY
 
Zurück