tinkerwellapp / drivers

All available Tinkerwell drivers
https://tinkerwell.app
MIT License
84 stars 34 forks source link

HTTP and Table Modes for non-laravel CMS #31

Closed bnomei closed 4 years ago

bnomei commented 4 years ago

I would like to add the HTTP (view rendering) and Table (csv data) to a non-laravel project. Its it possible with the current driver API?

CMS: getkirby.com

my current driver: https://gist.github.com/bnomei/90ad523dade93bc08e258cdf5dde04cf

mpociot commented 4 years ago

The HTTP view is framework dependent unfortunately. I don’t really know how I could make this driver specific. The table view does not depend on anything laravel specific though - as long as you are returning an array, it will be displayed as table data

bnomei commented 4 years ago

thanks for explaining about the table view. i got it working. but please do not write off the http mode support for non-laravel projects just yet... 😭

My suggestion

I put this first so in case you agree you can skip all the rest.

1) http view should load bootstrap from driver not default to laravel. should be easy to fix.

2) new overrideable driver methods. this is how they look for getkirby.

    // NEW driver method
    public function templateDirs(): array
    {
        return [
            // laravel 7
            // resource_path('views/layouts'),
            // resource_path('views/components'), // TADA: tinkerwell component support

            // getkirby
            kirby()->roots()->templates(),
            kirby()->roots()->snippets(),
        ];
    }

    // NEW driver method
    public function templateFilename(): string
    {
        return 'tinker.php'; // '__tinker__::tinker' in laravel
    }

How it works

as far as i can understand the http mode is 1) loading the app bootstrap 2) using a closure (in laravel the view helper with an array of data) 3) a html template string (blade syntax)

Screenshot 2020-03-07 at 10 08 56

What fails right now

Warning: require_once(PROJECT_ROOT/bootstrap/app.php): failed to open stream: 
No such file or directory in /Applications/Tinkerwell.app/Contents/Resources/tinkerwell/http.php on 
line 41 Fatal error: require_once(): Failed opening required 'PROJECT_ROOT/bootstrap/app.php' 
(include_path='.:') in /Applications/Tinkerwell.app/Contents/Resources/tinkerwell/http.php on line 41

what does not work right now for other CMS to do this as well is that in 1) it loads the laravel bootstrap and not what is defined in the driver. why? 2) it should not matter what string the closure returns, right? the bootstrapped cms just needs to be accessible inside the closure context. it could be a laravel response object but it could also be any other object that can be cast to a string or even a plain string, right? 3) that is a way to create a blade template with name of __tinker__::tinker on the fly.

Solutions

bootstrap

i can solve 1) in copying the cms default bootstrap (index.php to bootstrap/app.php). so if the http mode would load the bootstap from the driver that issue would be solved.

template

then another error pops up. this is the one i need your help with.

Error: Call to a member function make() on int in file /Applications/Tinkerwell.app/Contents/Resources/tinkerwell/http.php on line 55 Stack trace: 1. Error->() /Applications/Tinkerwell.app/Contents/Resources/tinkerwell/http.php:55

it fails to create the template file, which was to be expected. the only thing needed here are the following new driver params

closure

TL;DR

its needs to be a string or object that can be cast to string. most cms can provide that.

details

in getkirby cms you could return a response object but a more common way to "tinker" would be to use either a template or a snippet. a template is (in most cases) a php file at a certain directory. to parse it the php vars are injected.

Screenshot 2020-03-07 at 10 42 48

a snippet is pretty much like the view helper using the filename without the php extension. it does not use blade with curly brackets but makes the forwarded vars available as plain php vars again (see $title).

Screenshot 2020-03-07 at 10 24 02

i would love to see this feature working and will help as much as i can. 🙏

bnomei commented 4 years ago

if you are not in favor of creating (and removing) the tinker.php file from the templatedirs then an variable like $tinkerHTTPView could be forwarded to the render view. Most CMS have some way to render a template from a string so this would make an easy and versatile solution as well.

Screenshot 2020-03-07 at 11 50 14

mpociot commented 4 years ago

To give you a little background information on how the HTTP mode currently works (and why it is Laravel specific at the moment):

Tinkerwell allows you to write two separate pieces of data:

When you evaluate the HTTP view, here's what I do in the background:

So I do not actually make any real GET requests, since I don't really know about the applications URL. This all happens inside of Laravel.

I don't yet know how I could generalize this feature so that custom drivers could make use of it

bnomei commented 4 years ago

thanks for explaining in detail. kirby does not need the controller to be triggered as a route. it can be called just like the cli with the return value (or any echo if thats easier but i would prefer return to be consistent with laravel) to be dumped as html output.

template with file

what tinkerwell should do is write the view to a php file at a certain dir. getkirby does not need a route to get the html but the files need to be at certain dirs. executing the controller like in cli mode will do fine. the template files (one for templates and one for snippets) just need to be created and removed post execution.

side node: i made the dirs an array since it makes sense for kirby, but just having a single dir for templates would be ok-ish as well.


// expected in context of cms
return Str::load(
  kirby->roots()->templates() . '/tinker.php', 
  [
    'title' => 'Test',
  ]
);

// will work as well
return Str::load(
  $tinkerTmpFilePath,
  [
    'title' => 'Test',
  ]
);

// but for snippets this needs a file at a certain dir
return snippet('tinker', // kirby()->roots()->snippets() . '/tinker.php' 
    [
        'title' => 'Test',
    ], 
    true // would echo by default. so make it return
);

generalize

maybe the driver could just state if the controller should be either

// NEW driver method
public function canControllerReturnHtml(): bool
{
    // return false; // for laravel since it needs a route
    return true; // for for kirby since controller directly return html (just like in cli mode)
}