{"action":"created","issue":{"url":"https://api.github.com/repos/nova-framework/framework/issues/770","repository_url":"https://api.github.com/repos/nova-framework/framework","labels_url":"https://api.github.com/repos/nova-framework/framework/issues/770/labels{/name}","comments_url":"https://api.github.com/repos/nova-framework/framework/issues/770/comments","events_url":"https://api.github.com/repos/nova-framework/framework/issues/770/events","html_url":"https://github.com/nova-framework/framework/pull/770","id":150155337,"number":770,"title":"Heavily improve Core\\View, while completely preserving the API","user":{"login":"LuckyCyborg","id":13488485,"avatar_url":"https://avatars.githubusercontent.com/u/13488485?v=3","gravatar_id":"","url":"https://api.github.com/users/LuckyCyborg","html_url":"https://github.com/LuckyCyborg","followers_url":"https://api.github.com/users/LuckyCyborg/followers","following_url":"https://api.github.com/users/LuckyCyborg/following{/other_user}","gists_url":"https://api.github.com/users/LuckyCyborg/gists{/gist_id}","starred_url":"https://api.github.com/users/LuckyCyborg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LuckyCyborg/subscriptions","organizations_url":"https://api.github.com/users/LuckyCyborg/orgs","repos_url":"https://api.github.com/users/LuckyCyborg/repos","events_url":"https://api.github.com/users/LuckyCyborg/events{/privacy}","received_events_url":"https://api.github.com/users/LuckyCyborg/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":18,"created_at":"2016-04-21T18:32:05Z","updated_at":"2016-04-21T23:41:47Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/nova-framework/framework/pulls/770","html_url":"https://github.com/nova-framework/framework/pull/770","diff_url":"https://github.com/nova-framework/framework/pull/770.diff","patch_url":"https://github.com/nova-framework/framework/pull/770.patch"},"body":"# Disclaimer - NO API breaks included!\r\n\r\nWhile someone (I look to you, Jim) can have a shock looking on the source code of the **Core\\View** proposed by this pull-request, as in being completely rewritten, the API compatibility to actual Core\\View is complete preserved. To be read:\r\n\r\n### This pull request does NOT introduce API breaks and the existent code will continue to work with NO changes.\r\n\r\n## Rationale\r\n\r\nEveryone love the Views rendering triplets. Me too. You know, that **< template header >< view >< template footer >**, but when you need a more complex structure, containing blocks, diverse partials and fragments, that all by-hand rendering can become complicated, even with heavy use of recent introduced **View::fetch()**.\r\n\r\nAnd that complicated thing should be repeated every Method, from every Controller from your Frontend or Backend.\r\n\r\nSure, one can ask: but, while having at our disposition two stacked Middleware, on Routing and Controllers, why we can't compose our Views and instruct the Middleware to do the final job?\r\n\r\nResponse: yes, we can do that in some measure, but almost **View** rendering methods (excluding **fetch**) have an immediate effect, being static functions, arriving to the need of building and passing over complex data arrays, or doing another Code-Fu...\r\n\r\nI'm of opinion that offering to end-user the ability to manipulate Views using a High Level API could simplify heavily the work with complex Page structures, like my own experience demonstrated me.\r\n\r\nThis is the reason of this pull-request and the proposed **Core\\View**, to offer a fluent and powerful API to manipulate the Views and their data, with a strong **Laravel-esque** taste.\r\n\r\n## Basic Commands \r\n\r\nWhile the actual Core\\View methods are static and they should called independently, the new API works with View **instances**, then we should build them. We have two methods at disposition, for standard Views and Templates one. An combined usage example is presented bellow:\r\n\r\n```php\r\nView::make('Welcome/SubPage')\r\n    ->shares('title', $title)\r\n    ->with($data)\r\n    ->render();\r\n\r\n// OR\r\n\r\n$page = View::make('Welcome/SubPage') ->with($data);\r\n\r\nView::makeTemplate('default')\r\n    ->shares('title', $title)\r\n    ->withContent($page)\r\n    ->display();\r\n```\r\n\r\nProbably you'll notice that we passed the first View instance ($page) as is to second one. That will make it to be automatically fetched to **$content** variable, on final rendering. Also, you will notice that the View Methods can be chained.\r\n\r\nThe data can be passed to a View instance in diverse ways. The following commands are equvalent:\r\n\r\n```php\r\n$page = View::make('Welcome/SubPage');\r\n\r\n$page->with('shinyInfo', $info);\r\n\r\n$page->withShinyInfo($info);\r\n\r\n$page->shinyInfo = $info;\r\n\r\n$page['shinyInfo'] = $info;\r\n```\r\n\r\nTo note the variable name transformation by dynamic **withX** methods.\r\n\r\nAlso, the View instances can be nested. The following command are equivalent:\r\n\r\n```php\r\n// Add a View instance to a View's data\r\n$view = View::make('foo')->nest('footer', 'Partials/Footer');\r\n\r\n// Equivalent functionality using the \"with\" method\r\n$view = View::make('foo')->with('footer', View::make('Partials/Footer'));\r\n```\r\n\r\nTo note that nesting assume that the nested View instance is a Standard View, not a Template one.\r\n\r\nThere is also a new **shares()** method, similar with actual **share()** but working for instances.\r\n\r\nAs rendering commands, we have:\r\n\r\n**fetch()**  : will render the View and return the output\r\n**render()**  : will render and output the View and display the output\r\n**display()**  : same as **display()** but will send also the Headers.\r\n\r\n## Simple Usage\r\n\r\n#### NOTE: the Triplet Style Rendering works as always, being full supported.\r\n\r\nThen you do not need to change your already written code. The following example is from an untouched Welcome's Controller method:\r\n\r\n```php\r\nView::renderTemplate('header', $data);\r\nView::render('Welcome/Welcome', $data);\r\nView::renderTemplate('footer', $data);\r\n```\r\n\r\nThis pull request introduce also a simple method of auto-rendering, which enter automatically in action when you return from a Controller Method execution a View instance. \r\n\r\nThen is enough for an usual template-based rendering to have a command like:\r\n\r\n```php\r\nreturn View::make('Welcome/SubPage', $data)->shares('title', $title);\r\n```\r\n\r\nPractically, that rendering style will execute also in Controller's **after()** the command:\r\n\r\n```php\r\nView::makeTemplate($this->layout, array(), $this->template)\r\n    ->withContent($result)\r\n    ->display();\r\n``` \r\n\r\nEvery Controller have now the ability to specify its Template and Layout, as following:\r\n\r\n```php\r\nclass Welcome extends Controller\r\n{\r\n    protected $template = 'Admin';\r\n    protected $layout = 'custom';\r\n\r\n    /**\r\n     * Call the parent construct\r\n     */\r\n    public function __construct()\r\n    {\r\n        parent::__construct();\r\n        $this->language->load('Welcome');\r\n    }\r\n\r\n    ...\r\n}\r\n```\r\n\r\n\r\nWHERE the 'default' Layout is a simple composition of your header/footer files. For details, see:\r\n\r\n```\r\napp/Templates/Default/default.php\r\n```\r\nThat file contains only:\r\n```php\r\nrequire dirname(__FILE__) .'/header.php';\r\n\r\necho $content;\r\n\r\nrequire dirname(__FILE__) .'/footer.php';\r\n```\r\nThen, any changes in your header and footer files will be propagated.\r\n\r\n# Advanced Usage\r\n\r\n## Rendering with partials (i.e. header/footer) from the standard Views location  \r\n\r\nIf you need to work with partials, for example blocks, header and footer files located in app/Views directory, it is very simple to do that. You have just to compose your views as following:\r\n\r\nThe following examples use a very simple Template Layout file, called:\r\n```\r\napp/Templates/Default/custom.php\r\n```\r\nwhich contains:\r\n```php\r\necho $header;\r\necho $content;\r\necho $footer;\r\n```\r\n\r\n### Rendering with a complete custom Template living on Views folder\r\n\r\n```php\r\nreturn View::makeTemplate('custom')\r\n   ->shares('title', $title)\r\n   ->with('header', View::make('Partials/Header'))\r\n   ->with('content', View::make('Page/Index', $data))\r\n   ->with('footer', View::make('Partials/Footer'));\r\n```\r\nOR\r\n```php\r\nreturn View::makeTemplate('custom')\r\n   ->shares('title', $title)\r\n   ->withHeader(View::make('Partials/Header'))\r\n   ->withContent(View::make('Page/Index', $data))\r\n   ->withFooter(View::make('Partials/Footer'));\r\n```\r\nOR\r\n```php\r\nreturn View::makeTemplate('custom')\r\n   ->shares('title', $title)\r\n   ->nest('header', 'Partials/Header')\r\n   ->nest('content', 'Page/Index', $data)\r\n   ->nest('footer', 'Partials/Footer');\r\n```\r\nOR\r\n```php\r\nreturn View::makeTemplate('custom')\r\n   ->shares('title', $title)\r\n   ->with('content', View::make('Partials/Layout')\r\n       ->nest('content', 'Page/Index', $data));\r\n```\r\n\r\n### Rendering in the Triplet Style, but using the new API\r\n\r\n```php\r\nreturn View::makeTemplate('custom')\r\n   ->shares('title', $title)\r\n   ->with('header', View::makeTemplate('header'))\r\n   ->with('content', View::make('Page/Index', $data))\r\n   ->with('footer', View::makeTemplate('footer'))\r\n```\r\n\r\n### Rendering in the Triplet Style, but using some Views as blocks\r\n\r\n```php\r\n// Views instances automatically rendered into base View rendering.\r\n$data['latestNewsBlock'] = View::make('Articles/LatestNewsBlock')->withNews($latestNews);\r\n$data['topVisitedBlock'] = View::make('Articles/TopVisitedBlock')->withNews($topNews);\r\n\r\nView::share('title', $title);\r\n\r\nView::renderTemplate('header');\r\nView::render('Welcome/Welcome', $data); // <-- there the View instances from $data will be fetched automatically\r\nView::renderTemplate('footer');\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/nova-framework/framework/issues/comments/213161307","html_url":"https://github.com/nova-framework/framework/pull/770#issuecomment-213161307","issue_url":"https://api.github.com/repos/nova-framework/framework/issues/770","id":213161307,"user":{"login":"LuckyCyborg","id":13488485,"avatar_url":"https://avatars.githubusercontent.com/u/13488485?v=3","gravatar_id":"","url":"https://api.github.com/users/LuckyCyborg","html_url":"https://github.com/LuckyCyborg","followers_url":"https://api.github.com/users/LuckyCyborg/followers","following_url":"https://api.github.com/users/LuckyCyborg/following{/other_user}","gists_url":"https://api.github.com/users/LuckyCyborg/gists{/gist_id}","starred_url":"https://api.github.com/users/LuckyCyborg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LuckyCyborg/subscriptions","organizations_url":"https://api.github.com/users/LuckyCyborg/orgs","repos_url":"https://api.github.com/users/LuckyCyborg/repos","events_url":"https://api.github.com/users/LuckyCyborg/events{/privacy}","received_events_url":"https://api.github.com/users/LuckyCyborg/received_events","type":"User","site_admin":false},"created_at":"2016-04-21T23:41:47Z","updated_at":"2016-04-21T23:41:47Z","body":"Oh, i applied a porting of one of the Welcome methods to the new style\r\n\r\nJust download the patch, apply it and see the thing."}}