GithubHelp home page GithubHelp logo

Comments (8)

ragboyjr avatar ragboyjr commented on June 1, 2024

Good find.

My gut tells me that this should be using the parent section content when pushing or not or define its own. I think this should be doable to implement.

from plates.

odahcam avatar odahcam commented on June 1, 2024

Hey glad to see you're back!

I did some debugging in the past, but it's been too long, but I want to make some context for others reading this.

We have the fetch method and it just renders the child template (ie item.phtml) and takes the string output.

/**
* Fetch a rendered template.
* @param string $name
* @param array $data
* @return string
*/
public function fetch($name, array $data = array())
{
return $this->engine->render($name, $data);
}

The child template (.phtml) should be able to register the sections in the parent scope, details.phtml because since it was the target to ->render(...) Plates is creating a Template object which we're refering to as the scope and fetch method called is actually from it. But the fetch is actually using the engine render method which AFAIK will create a new Template object for the child items.phtml.

Given that I think a possible solution would be to code the fetch method something like this to have access to the child's metadata:

     public function fetch($name, array $data = array()) 
     { 
          $template = $this->engine->make($name); 
          $this->sections = array_merge($this->sections, $template->sections);
          // here we could grab more info from the child
          return $template->render($data);
     } 

I'm assuming a high risk here of talking something completely nonsense hahaha, but this seems to make sense.

from plates.

odahcam avatar odahcam commented on June 1, 2024

Yeah, my idea above would never work, but I made some tests and implemented some working code in #306. There's a section mode issue though, described in the PR, that would be very nice if you could give your opinion.

from plates.

odahcam avatar odahcam commented on June 1, 2024

I think I covered everything I wanted at this point, it would be nice to have your input on the PR, thanks!

from plates.

basteyy avatar basteyy commented on June 1, 2024

And I thought I would be the only one with the use case ;) Thanks for the efforts so far. I like the input #306 (comment) from ragboyjr.

This is a (quick and dirty) fix (based on extensions) for my use case, where I want to push some script data to the layout:

// Register Extension
// file: e.g. index.php (somewhere near the engine definition)
$engine->loadExtension(new class implements ExtensionInterface {
        private string $content = '';
        public function register(Engine $engine)
        {
            $engine->registerFunction('addToSection', function($content) {
                $this->content .= ' ' . $content;
            });
            $engine->registerFunction('getSection', function () {
                return $this->content;
            });
        }
    });

// Add some data
// file home.php
$this->addToSection(<<<EOD
<script>
alert("Hello World");
</script>
EOD);

// Output somewhere in the layout (or any other template)
// file: layout.php
echo $this->getSection();

from plates.

odahcam avatar odahcam commented on June 1, 2024

@basteyy ohhh so that's why there are extensions like that! I wasn't getting the point of using something like it but now it's clear. Did any of you check my latest changes in the PR? It's actually looking good now, with clear APIs and automated tests. I really want some input to have this in the next Plates version as soon as possible.

from plates.

charlesgetup avatar charlesgetup commented on June 1, 2024

Hi guys,

Since this issue is still open, here is my solution. Just a thought for you guys to have a look.

In my situation, I have

  • admin.php (Admin layout)
  • users.php (User page which inherits the admin layout)
  • pagination.php (page partial which only holds the user table pagination and it is used by users.php)

And I have the following push function in pagination.php.

<?php $this->push('scripts') ?>
    <script src="/assets/pagination.js"></script>
<?php $this->end() ?>

So if I use insert/fetch function in users.php, the pagination.js is not loaded by users.php. I think that is the timing issue. When the partial (pagination.php) is rendered, the user page (users.php) has not rendered yet. So the pagination.js could not be rendered to layout's scripts section.

My fix is **include** the partial, not render it immediately. Let the partial render together with the User page (users.php). Then the problem has been solved.

Solution:

In users.php, write,

<?php **include** $this->getPath('admin/partials/pagination'); ?>

The getPath is a helper function which gets the template file path. In this way, I have no need to change the core code.

One disadvantage is adding variable(s) to the nested template. For example, in my User page, I have a variable called $userPaginationLimit. And in the pagination template, I want to use it as $limit. If I use insert/fetch function, I could write it like, $this->insert('admin/partials/pagination', array('limit' => $userPaginationLimit));. But in my solution, I have to write $limit = $userPaginationLimit; include $this->getPath('admin/partials/pagination'); . So to make my code cleaner, I have to avoid this variable naming inconsistent situation.

from plates.

charlesgetup avatar charlesgetup commented on June 1, 2024

I spent another day on this issue and I believe that this is a design "issue". The "issue" happens at the render function in template.php file.

public function render(array $data = array())
{
$this->data($data);
$path = ($this->engine->getResolveTemplatePath())($this->name);
try {
$level = ob_get_level();
ob_start();
(function() {
extract($this->data);
include func_get_arg(0);
})($path);
$content = ob_get_clean();
if (isset($this->layoutName)) {
$layout = $this->engine->make($this->layoutName);
$layout->sections = array_merge($this->sections, array('content' => $content));
$content = $layout->render($this->layoutData);
}
return $content;
} catch (Throwable $e) {
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
}
}

I will continue using my example in the above comment here to explain my findings.

When using the insert/fetch function in a partial, it eventually calls this render function. From the render function, we can see, line #174, it requires layoutName to stack the sections. But in a partial, there is no layout referenced like below. Then take a closer look at the render function, it uses the "ob_*" functions to display the nested templates from inside out. So the partial will be rendered first. That means when the partial is first rendered, there is no way for it to have a layout. That is why the insert/fetch` function in a partial won't work.

<?php $this->layout('template', ['title' => 'Users']) ?>

My thought is adding the above line of code to the partial, then the partial has a layout name, and it could push/unshift code the the parent page sections. But this also won't work. No matter which template is used as layout in a partial, it will be rendered together with the partial, this may duplicate some of the page content, and all the variables passed to that layout need to pass to partial, too. And this will block the partial to be reused by other pages.

So to fix this "issue", I think we have to re-design the render function and find a proper way to pass the layout to the partial or render the partial in the right time. And I failed to make this change yesterday. This could be a big move and affect everything in the project. Finally, I have to come back to my solution in the above comment.

When I have a second thought of the include line of code, I found that it won't work when the partial needs to be included multiple times. In my example, if I want to include the pagination twice for users and user roles, I have to write the following code and the variables with same name will override each other.

...
$limit = $userLimit;
$total = $userAmount;
include $this->getPath('admin/partials/pagination');
...
$limit = $userRoleLimit;
$total = $userRoleAmount;
include $this->getPath('admin/partials/pagination');

In this comment, I made an improvement of the solution. Make it not that dirty and fix the variable same name issue.

The improved solution has two parts.

1, A new helper function. It will be used to replace the "insert/fetch" function. Here is the helper I wrote, a simplified version. You could add your logic in it.

function includePartial($name, $variables = array())
{
    // Extract the variables
    extract($variables);

    // Start output buffering
    ob_start();

    // Include the template file.
    // The "getPath" function is another help function which get template file path by its name.
    include $this->getPath($name);

    // This is about 2nd part, run the "push/unshift" function in partial when parent page is rendered.
    if(function_exists('loadDependencies')){
        loadDependencies($this->template); // You may pass some more variables to this function
    }

    // End buffering and render partial contents
    $output = ob_get_clean();
    
    echo $output;
}

2, Implement "loadDependencies" function in partial. In my example, on pagination.php file, the function is implemented like below.

<?php
function loadDependencies ($template) {
    $template->push('scripts');
?>
    <script src="/assets/js/pagination.js"></script>
<?php
    $template->end();
}
?>

Then in the parent page, the user.php file, I include the partial like below. And this will look nicer in some point.

<?php
    $this->includePartial('admin/partials/pagination', array('limit' => $userLimit, 'total' => $userAmount));
?>

I know this solution is not perfect, and if I have some better idea, I will make comment again (if this issue is still open, 😃).

Conclusion:

I like the ob_* functions way to render the nested page content. Very efficient. So if we don't call the template functions, e.g. push/unshift, within the template which has no layout, it runs perfectly. I think that is the design. We should use the template functions in a template with a layout. The only confusion is this is not mentioned in the document. I hope the author could explain this more clearly in the document inheritance page.

A very nice project, great work. I will keep using it and thanks the author for sharing. 👍

from plates.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.