Search code examples
phphtmllaraveltinymcelaravel-9

Displaying Content Dynamically Based on <h2> Tags with @php command in Laravel 9: Local vs. Hostinger Hosting Issues


I have a Laravel 9 application that I'm hosting on a shared hosting with PHP 8.3.

I have a tinyMCE text editor in my admin panel that I want to show the content sent from it in two different ways in my front-end. These ways are:

  1. If there is less than two <h2> elements in the content sent to the view, the content will be shown as it is and there won't be a sticky side navbar for each section, like the image below: Content with one  element

  2. But if there is at least two <h2> elements in the sent content there will be a sticky navbar in the left with links to each section which consists of an <h2> element and the elements after it until the next <h2> element or the end of the content just like the image below:

Content with at least two  elements

I have some stylings and JS codes that are responsible for hiding the navbar on small devices and change the order of some elements such as showing the keywords (tags) at the bottom on small devices but these are not that important when trying to fix this issue.

So far, I've tried a code like below:

<div class="row">
    <!-- Sidebar -->
    <div class="col-md-3">
        <div class="title-sm">
            <h2>
                {{$service->title}}
            </h2>
            <ul class="entry-meta">
                <li>{{ $service->updated_at->format('F d, Y') }}</li>
            </ul>
        </div>
        <div class="sidebar">
            <h2>
                {{$service->title}}
            </h2>
            <ul class="entry-meta">
                <li>{{ $service->updated_at->format('F d, Y') }}</li>
            </ul>
            @php
            $dom = new DomDocument();

            @$dom->loadHTML(mb_convert_encoding($service->content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
            $h2Elements = $dom->getElementsByTagName('h2');
            $sections = [];


            foreach ($h2Elements as $index => $h2) {
            $id = 'section' . ($index + 1); // Create an ID for the section
            $sections[$id] = ['title' => $h2->nodeValue, 'content' => ''];
            // Gather content until the next H2 or end of the document
            $node = $h2->nextSibling;
            while ($node) {
            if ($node instanceof DOMElement && $node->tagName == 'h2') {
            break;
            }
            $sections[$id]['content'] .= $dom->saveHTML($node);
            $node = $node->nextSibling;
            }
            }
            @endphp

            @if(count($sections) > 1)
            <ul id="content-navbar" class="list-group list-group-light border py-0">
                @foreach($sections as $id => $section)
                <li class="list-group-item border-0 py-0"><a href="#{{ $id }}" class="nav-link py-0">{{ $section['title'] }}</a></li>
                @endforeach
            </ul>
            @if($service->meta_keywords)
            @php
            $metaKeywords = explode(",", trim($service->meta_keywords));
            @endphp
            <div class="entry-tags">
                <h6>Anahtar Kelimeler:</h6>
                @foreach($metaKeywords as $metaKeyword)
                <span class="badge badge-light">{{$metaKeyword}} </span>
                @endforeach
            </div>
            @endif
            @endif
        </div>

    </div>
    @if(count($sections) > 1)
    <div class="col-md-8 main-content">
        @foreach($sections as $id => $section)
        <section id="{{ $id }}">
            <h2>{{ $section['title'] }}</h2>
            {!! $section['content'] !!}
        </section>
        @endforeach
    </div>
    @else
    <div class="col-md-12 main-content">
        {!! $service->content !!}
        <div class="keywords-md">
            @if($service->meta_keywords)
            @php
            $metaKeywords = explode(",", trim($service->meta_keywords));
            @endphp
            <div class="entry-tags">
                <h6>Anahtar Kelimeler:</h6>
                @foreach($metaKeywords as $metaKeyword)
                <span class="badge badge-light">{{$metaKeyword}} </span>
                @endforeach
            </div>
            @endif
        </div>
    </div>

    @endif


    <!-- Content -->

    <div class="keywords-sm">
        @if($service->meta_keywords)
        @php
        $metaKeywords = explode(",", trim($service->meta_keywords));
        @endphp
        <div class="entry-tags">
            <h6>Anahtar Kelimeler:</h6>
            @foreach($metaKeywords as $metaKeyword)
            <span class="badge badge-light">{{$metaKeyword}} </span>
            @endforeach
        </div>
        @endif
    </div>
</div>

On my local machine, everything is OK but when I try to deploy the project on a shared hosting (Hostinger), for some reason the content of first section is being shown as the first navbar item. I mean If my first section has an element with "One" text inside it and a <p> element right after it with "This is one" text, I expect to see a navbar item like "One" but I see "OneThis is oneOneThis is oneTwoThis is twoThreeThis is threeFourThis is four". Also, the first section gets only a heading with the same text just like the image below:

How it's shown on my shared hosting

Why is this OK on my local but problematic on my shared hosting? How can I solve this?


Solution

  • The issue was related to the handling of DOM operations and encoding differences between my local environment and the shared hosting environment.

    I solved the issue by installing the following composer package:

    composer require voku/simple_html_dom
    

    And then changing the code simply like below:

    <div class="row">
        <!-- Sidebar -->
        <div class="col-md-3">
            <div class="title-sm">
                <h2>{{ $service->title }}</h2>
                <ul class="entry-meta">
                    <li>{{ $service->updated_at->format('F d, Y') }}</li>
                </ul>
            </div>
            <div class="sidebar">
                <h2>{{ $service->title }}</h2>
                <ul class="entry-meta">
                    <li>{{ $service->updated_at->format('F d, Y') }}</li>
                </ul>
                @php
                use voku\helper\HtmlDomParser;
                $html = HtmlDomParser::str_get_html($service->content);
                $h2Elements = $html->find('h2');
                $sections = [];
                foreach ($h2Elements as $index => $h2) {
                $id = 'section' . ($index + 1); // Create an ID for the section
                $sections[$id] = ['title' => $h2->plaintext, 'content' => ''];
                // Gather content until the next H2 or end of the document
                $node = $h2->next_sibling();
                while ($node) {
                if ($node->tag === 'h2') {
                break;
                }
                $sections[$id]['content'] .= $node->outertext;
                $node = $node->next_sibling();
                }
                }
                @endphp
    
                @if(count($sections) > 1)
                <ul id="content-navbar" class="list-group list-group-light border py-0">
                    @foreach($sections as $id => $section)
                    <li class="list-group-item border-0 py-0"><a href="#{{ $id }}" class="nav-link py-0">{{ $section['title'] }}</a></li>
                    @endforeach
                </ul>
                @if($service->meta_keywords)
                @php
                $metaKeywords = explode(",", trim($service->meta_keywords));
                @endphp
                <div class="entry-tags">
                    <h6>Anahtar Kelimeler:</h6>
                    @foreach($metaKeywords as $metaKeyword)
                    <span class="badge badge-light">{{ $metaKeyword }}</span>
                    @endforeach
                </div>
                @endif
                @endif
            </div>
        </div>
        @if(count($sections) > 1)
        <div class="col-md-8 main-content">
            @foreach($sections as $id => $section)
            <section id="{{ $id }}">
                <h2>{{ $section['title'] }}</h2>
                {!! $section['content'] !!}
            </section>
            @endforeach
        </div>
        @else
        <div class="col-md-12 main-content">
            {!! $service->content !!}
            <div class="keywords-md">
                @if($service->meta_keywords)
                @php
                $metaKeywords = explode(",", trim($service->meta_keywords));
                @endphp
                <div class="entry-tags">
                    <h6>Anahtar Kelimeler:</h6>
                    @foreach($metaKeywords as $metaKeyword)
                    <span class="badge badge-light">{{ $metaKeyword }}</span>
                    @endforeach
                </div>
                @endif
            </div>
        </div>
        @endif
    
        <!-- Content -->
        <div class="keywords-sm">
            @if($service->meta_keywords)
            @php
            $metaKeywords = explode(",", trim($service->meta_keywords));
            @endphp
            <div class="entry-tags">
                <h6>Anahtar Kelimeler:</h6>
                @foreach($metaKeywords as $metaKeyword)
                <span class="badge badge-light">{{ $metaKeyword }}</span>
                @endforeach
            </div>
            @endif
        </div>
    </div>