<?php
-namespace App;
+namespace App\Controllers;
use Sober\Controller\Controller;
<?php
-namespace App;
+namespace App\Controllers;
use Sober\Controller\Controller;
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "195334f043161c2cb8dfd8010465e82f",
+ "content-hash": "e309eb64deedb0a5e56f3f04607dd38d",
"packages": [
{
"name": "brain/hierarchy",
],
"time": "2018-01-09T20:05:19+00:00"
},
- {
- "name": "hassankhan/config",
- "version": "0.10.0",
- "source": {
- "type": "git",
- "url": "https://github.com/hassankhan/config.git",
- "reference": "06ac500348af033f1a2e44dc357ca86282626d4a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/hassankhan/config/zipball/06ac500348af033f1a2e44dc357ca86282626d4a",
- "reference": "06ac500348af033f1a2e44dc357ca86282626d4a",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.0"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.0",
- "scrutinizer/ocular": "~1.1",
- "squizlabs/php_codesniffer": "~2.2"
- },
- "suggest": {
- "symfony/yaml": "~2.5"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Noodlehaus\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Hassan Khan",
- "homepage": "http://hassankhan.me/",
- "role": "Developer"
- }
- ],
- "description": "Lightweight configuration file loader that supports PHP, INI, XML, JSON, and YAML files",
- "homepage": "http://hassankhan.me/config/",
- "keywords": [
- "config",
- "configuration",
- "ini",
- "json",
- "microphp",
- "unframework",
- "xml",
- "yaml",
- "yml"
- ],
- "time": "2016-02-11T16:21:17+00:00"
- },
{
"name": "illuminate/config",
"version": "v5.6.21",
"email": "adam.wathan@gmail.com"
}
],
+ "abandoned": "blade-ui-kit/blade-icons",
"time": "2017-08-13T20:55:40+00:00"
},
{
},
{
"name": "soberwp/controller",
- "version": "9.0.0-beta.4",
+ "version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/soberwp/controller.git",
- "reference": "2b6c8450f4a3100b16bfc482c825d89422b6adc6"
+ "reference": "1b2a71df0eb6d82cef10ed528a3e71cab40096b5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/soberwp/controller/zipball/2b6c8450f4a3100b16bfc482c825d89422b6adc6",
- "reference": "2b6c8450f4a3100b16bfc482c825d89422b6adc6",
+ "url": "https://api.github.com/repos/soberwp/controller/zipball/1b2a71df0eb6d82cef10ed528a3e71cab40096b5",
+ "reference": "1b2a71df0eb6d82cef10ed528a3e71cab40096b5",
"shasum": ""
},
"require": {
"brain/hierarchy": "^2.3",
- "composer/installers": "~1.0",
- "hassankhan/config": "^0.10.0",
- "php": ">=5.6.0",
- "symfony/yaml": "^3.2"
+ "composer/installers": "^1.5",
+ "php": ">=5.6.0"
},
"require-dev": {
- "squizlabs/php_codesniffer": "2.*"
+ "squizlabs/php_codesniffer": "^3.2"
},
"type": "package",
"autoload": {
"psr-4": {
- "Sober\\Controller\\": "src/",
- "Sober\\Controller\\Module\\": "src/Module/"
+ "Sober\\Controller\\": "src/"
},
"files": [
"controller.php"
"keywords": [
"wordpress"
],
- "time": "2017-08-22T17:35:30+00:00"
+ "time": "2018-08-17T08:45:11+00:00"
},
{
"name": "symfony/debug",
"time": "2018-04-30T01:23:47+00:00"
},
{
- "name": "symfony/yaml",
- "version": "v3.4.9",
+ "name": "wikimedia/relpath",
+ "version": "2.1.1",
"source": {
"type": "git",
- "url": "https://github.com/symfony/yaml.git",
- "reference": "033cfa61ef06ee0847e056e530201842b6e926c3"
+ "url": "https://github.com/wikimedia/RelPath.git",
+ "reference": "35e701ff16abf461bb8676a9d9177f86fa0b2c94"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/033cfa61ef06ee0847e056e530201842b6e926c3",
- "reference": "033cfa61ef06ee0847e056e530201842b6e926c3",
+ "url": "https://api.github.com/repos/wikimedia/RelPath/zipball/35e701ff16abf461bb8676a9d9177f86fa0b2c94",
+ "reference": "35e701ff16abf461bb8676a9d9177f86fa0b2c94",
"shasum": ""
},
"require": {
- "php": "^5.5.9|>=7.0.8"
- },
- "conflict": {
- "symfony/console": "<3.4"
+ "php": ">=5.5.9"
},
"require-dev": {
- "symfony/console": "~3.4|~4.0"
- },
- "suggest": {
- "symfony/console": "For validating YAML files using the lint command"
+ "jakub-onderka/php-parallel-lint": "^0.9.0.0",
+ "mediawiki/mediawiki-codesniffer": "15.0.0",
+ "phpunit/phpunit": "^4.8.9.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
"autoload": {
- "psr-4": {
- "Symfony\\Component\\Yaml\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
+ "files": [
+ "src/RelPath/RelPath.php",
+ "src/Wikimedia/RelPath.php"
]
},
"notification-url": "https://packagist.org/downloads/",
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
+ "name": "Ori Livneh",
+ "email": "ori@wikimedia.org"
}
],
- "description": "Symfony Yaml Component",
- "homepage": "https://symfony.com",
- "time": "2018-04-08T08:21:29+00:00"
- },
+ "description": "Compute a relative filepath between two paths.",
+ "homepage": "https://www.mediawiki.org/wiki/RelPath",
+ "time": "2018-01-18T21:23:40+00:00"
+ }
+ ],
+ "packages-dev": [
{
- "name": "wikimedia/relpath",
- "version": "2.1.1",
+ "name": "hassankhan/config",
+ "version": "0.10.0",
"source": {
"type": "git",
- "url": "https://github.com/wikimedia/RelPath.git",
- "reference": "35e701ff16abf461bb8676a9d9177f86fa0b2c94"
+ "url": "https://github.com/hassankhan/config.git",
+ "reference": "06ac500348af033f1a2e44dc357ca86282626d4a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/wikimedia/RelPath/zipball/35e701ff16abf461bb8676a9d9177f86fa0b2c94",
- "reference": "35e701ff16abf461bb8676a9d9177f86fa0b2c94",
+ "url": "https://api.github.com/repos/hassankhan/config/zipball/06ac500348af033f1a2e44dc357ca86282626d4a",
+ "reference": "06ac500348af033f1a2e44dc357ca86282626d4a",
"shasum": ""
},
"require": {
- "php": ">=5.5.9"
+ "php": ">=5.3.0"
},
"require-dev": {
- "jakub-onderka/php-parallel-lint": "^0.9.0.0",
- "mediawiki/mediawiki-codesniffer": "15.0.0",
- "phpunit/phpunit": "^4.8.9.0"
+ "phpunit/phpunit": "~4.0",
+ "scrutinizer/ocular": "~1.1",
+ "squizlabs/php_codesniffer": "~2.2"
+ },
+ "suggest": {
+ "symfony/yaml": "~2.5"
},
"type": "library",
"autoload": {
- "files": [
- "src/RelPath/RelPath.php",
- "src/Wikimedia/RelPath.php"
- ]
+ "psr-4": {
+ "Noodlehaus\\": "src"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
],
"authors": [
{
- "name": "Ori Livneh",
- "email": "ori@wikimedia.org"
+ "name": "Hassan Khan",
+ "homepage": "http://hassankhan.me/",
+ "role": "Developer"
}
],
- "description": "Compute a relative filepath between two paths.",
- "homepage": "https://www.mediawiki.org/wiki/RelPath",
- "time": "2018-01-18T21:23:40+00:00"
- }
- ],
- "packages-dev": [
+ "description": "Lightweight configuration file loader that supports PHP, INI, XML, JSON, and YAML files",
+ "homepage": "http://hassankhan.me/config/",
+ "keywords": [
+ "config",
+ "configuration",
+ "ini",
+ "json",
+ "microphp",
+ "unframework",
+ "xml",
+ "yaml",
+ "yml"
+ ],
+ "time": "2016-02-11T16:21:17+00:00"
+ },
{
"name": "illuminate/console",
"version": "v5.6.21",
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2018-04-03T05:22:50+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v3.4.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "033cfa61ef06ee0847e056e530201842b6e926c3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/033cfa61ef06ee0847e056e530201842b6e926c3",
+ "reference": "033cfa61ef06ee0847e056e530201842b6e926c3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8"
+ },
+ "conflict": {
+ "symfony/console": "<3.4"
+ },
+ "require-dev": {
+ "symfony/console": "~3.4|~4.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "https://symfony.com",
+ "time": "2018-04-08T08:21:29+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
- "roots/sage-lib": 10,
- "soberwp/controller": 10
+ "roots/sage-lib": 10
},
"prefer-stable": false,
"prefer-lowest": false,
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
- 'Sober\\Controller\\Module\\' => array($vendorDir . '/soberwp/controller/src/Module'),
'Sober\\Controller\\' => array($vendorDir . '/soberwp/controller/src'),
'Roots\\Sage\\Installer\\' => array($vendorDir . '/roots/sage-installer/src'),
'Roots\\Sage\\' => array($vendorDir . '/roots/sage-lib'),
'Symfony\\Component\\Finder\\' => 25,
'Symfony\\Component\\Debug\\' => 24,
'Symfony\\Component\\Console\\' => 26,
- 'Sober\\Controller\\Module\\' => 24,
'Sober\\Controller\\' => 17,
),
'R' =>
array (
0 => __DIR__ . '/..' . '/symfony/console',
),
- 'Sober\\Controller\\Module\\' =>
- array (
- 0 => __DIR__ . '/..' . '/soberwp/controller/src/Module',
- ),
'Sober\\Controller\\' =>
array (
0 => __DIR__ . '/..' . '/soberwp/controller/src',
"name": "Adam Wathan",
"email": "adam.wathan@gmail.com"
}
- ]
+ ],
+ "abandoned": "blade-ui-kit/blade-icons"
},
{
"name": "psr/container",
},
{
"name": "soberwp/controller",
- "version": "9.0.0-beta.4",
- "version_normalized": "9.0.0.0-beta4",
+ "version": "2.1.0",
+ "version_normalized": "2.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/soberwp/controller.git",
- "reference": "2b6c8450f4a3100b16bfc482c825d89422b6adc6"
+ "reference": "1b2a71df0eb6d82cef10ed528a3e71cab40096b5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/soberwp/controller/zipball/2b6c8450f4a3100b16bfc482c825d89422b6adc6",
- "reference": "2b6c8450f4a3100b16bfc482c825d89422b6adc6",
+ "url": "https://api.github.com/repos/soberwp/controller/zipball/1b2a71df0eb6d82cef10ed528a3e71cab40096b5",
+ "reference": "1b2a71df0eb6d82cef10ed528a3e71cab40096b5",
"shasum": ""
},
"require": {
"brain/hierarchy": "^2.3",
- "composer/installers": "~1.0",
- "hassankhan/config": "^0.10.0",
- "php": ">=5.6.0",
- "symfony/yaml": "^3.2"
+ "composer/installers": "^1.5",
+ "php": ">=5.6.0"
},
"require-dev": {
- "squizlabs/php_codesniffer": "2.*"
+ "squizlabs/php_codesniffer": "^3.2"
},
- "time": "2017-08-22T17:35:30+00:00",
+ "time": "2018-08-17T08:45:11+00:00",
"type": "package",
"installation-source": "dist",
"autoload": {
"psr-4": {
- "Sober\\Controller\\": "src/",
- "Sober\\Controller\\Module\\": "src/Module/"
+ "Sober\\Controller\\": "src/"
},
"files": [
"controller.php"
vendor
-_
\ No newline at end of file
+dist
-### dev-master:
+### 2.1.0:
+* Update deps
+* Pass in field data from Acf Options under App class
+* Change $this->data from private to protected param
+* Fix $post bug not appearing in the $this->data
+* Fix Controller overriding filter $data
+* Add filter to return Acf data as array
+* Add __before and __after lifecycles
+* @code and @codeif
+
+### 2.0.1:
+* Fix bug assuming Controllers/ folder name
+
+### 2.0.0:
+* PSR4 loading
+* Template overrides for those underscores
+* Pass in field data from Acf automatically
+* Debugger to include static methods
+* Improve Debugger results
+* Dependency injection
+* Bug fixes
* Change default path from resources/controllers to app/controllers
### 9.0.0-beta.3:
# Controller
-WordPress package to enable a controller when using Blade with [Sage 9](https://roots.io/sage/).
+WordPress package to enable a controller when using Blade with [Sage](https://roots.io/sage/)
+
+* [Installation](#installation)
+* [Setup](#setup)
+* [Usage](#usage)
+ * [Overview](#overview)
+ * [Basic Controller](#basic-controller)
+ * [Using functions](#using-static-methods)
+ * [Using components](#creating-components)
+ * [Inheriting the tree/heirarchy](#inheriting-the-treeheirarchy)
+ * [Creating global properties](#creating-global-properties)
+ * [Advanced Custom Fields module](#advanced-custom-fields-module)
+ * [Template override option](#template-override-option)
+ * [Disable option](#disable-option)
+* [Debugging](#debugging)
+
+<br>
## Installation
-#### Composer:
+### Composer:
-**Please note that Controller is no longer an mu-plugin and is now a Composer theme depedency.**
-
-Browse into the Sage theme directory and run;
+[Sage](https://roots.io/sage/) ships with Controller. However, should you need to install, browse into the Sage theme directory and run;
```shell
-$ composer require soberwp/controller:9.0.0-beta.3
+$ composer require soberwp/controller:2.1.0
```
-#### Requirements:
+### Upgrading to 2.x.x:
+
+Please note that versions 2.x.x are newer releases than 9.x.x-beta. The 9 was used to match Sage 9 versioning at the time.
+
+Controller 2.x.x uses [PSR4 autoloading](https://www.php-fig.org/psr/psr-4/) to load Controller classes. This is considered best practice. You will need to [update the following files](https://github.com/roots/sage/pull/2025/files) from 9.0.0-beta versions.
+
+Folder `controllers/` changes to `Controllers/`, class file names changes to camelcase `App.php` and `FrontPage.php`. Controller namespaces changes to `namespace App\Controllers;`
+
+### Requirements:
* [PHP](http://php.net/manual/en/install.php) >= 7.0
## Setup
-By default, create folder `app/controllers/` within your theme directory.
+By default Controller uses namespace `Controllers`.
+
+Controller takes advantage of [PSR-4 autoloading](https://www.php-fig.org/psr/psr-4/). To change the namespace, use the filter below within `functions.php`
-Alternatively, you can define a custom path using the filter below within your themes `functions.php` file;
```php
-add_filter('sober/controller/path', function () {
- return dirname(get_template_directory()) . '/app/custom-folder';
+add_filter('sober/controller/namespace', function () {
+ return 'Data';
});
```
-The controller will autoload PHP files within the above path and its subdirectories.
-
## Usage
-#### Creating a basic Controller:
+### Overview:
-* Controller files follow the same hierarchy as WordPress.
- * You can view the controller hierarchy by using the Blade directive `@debug('hierarchy')`.
-* Extend the Controller Class— it is recommended that the class name matches the filename.
+* Controller class names follow the same hierarchy as WordPress.
+* The Controller class name should match the filename
+ * For example `App.php` should define class as `class App extends Controller`
* Create methods within the Controller Class;
- * Use `public function` to expose the returned values to the Blade views/s.
- * Use `public static function` to use the function within your Blade view/s.
- * Use `protected function` for internal controller methods as only public methods are exposed to the view. You can run them within `__construct`.
-* Return a value from the public methods which will be passed onto the Blade view.
- * **Important:** The method name is converted to snake case and becomes the variable name in the Blade view.
- * **Important:** If the same method name is declared twice, the latest instance will override the previous.
+ * Use `public function` to return data to the Blade views/s
+ * The method name becomes the variable name in Blade
+ * Camel case is converted to snake case. `public function ExampleForUser` in the Controller becomes `$example_for_user` in the Blade template
+ * If the same method name is declared twice, the latest instance will override the previous
+ * Use `public static function` to use run the method from your Blade template which returns data. This is useful for loops
+ * The method name is not converted to snake case
+ * You access the method using the class name, followed by the method. `public static function Example` in `App.php` can be run in Blade using `App::Example()`
+ * If the same method name is declared twice, the latest instance will override the previous
+ * Use `protected function` for internal methods. These will not be exposed to Blade. You can run them within `__construct`
+ * Dependency injection with type hinting is available through `__construct`
-#### Examples:
+
+The above may sound complicated on first read, so let's take a look at some examples to see how simple Controller is to use.
+
+### Basic Controller;
The following example will expose `$images` to `resources/views/single.blade.php`
-**app/controllers/Single.php**
+**app/Controllers/Single.php**
```php
<?php
-namespace App;
+namespace App\Controllers;
use Sober\Controller\Controller;
@endif
```
-#### Creating Components;
+### Using Functions;
-You can also create reusable components and include them in a view using PHP traits.
+You can use static methods to run a function from within your view.
-**app/controllers/partials/Images.php**
+This is useful if you are within the loop and want to return data for each post item.
+
+**app/Controllers/Archive.php**
```php
<?php
-namespace App;
+namespace App\Controllers;
-trait Images
+use Sober\Controller\Controller;
+
+class Archive extends Controller
{
- public function images()
+ public static function title()
{
- return get_field('images');
+ return get_post()->post_title;
}
}
```
-You can now include the Images trait into any view to pass on variable $images;
-
-**app/controllers/Single.php**
+**resources/views/archive.php**
```php
-<?php
+@extends('layouts.app')
-namespace App;
+@section('content')
-use Sober\Controller\Controller;
+ @while (have_posts()) @php(the_post())
+ {{ Archive::title() }}
+ @endwhile
-class Single extends Controller
-{
- use Images;
-}
+@endsection
```
-#### Using Static Methods;
-
-You can use static methods to return content from your controller.
+### Using Components;
-This is useful if you are within the loop and want to return data for each post item individually.
+You can also create reusable components and include them in any Controller class using PHP traits.
-**app/controllers/Archive.php**
+**app/Controllers/partials/Images.php**
```php
<?php
-namespace App;
-
-use Sober\Controller\Controller;
+namespace App\Controllers\Partials;
-class Archive extends Controller
+trait Images
{
- public static function title()
+ public function images()
{
- return get_post()->post_title;
+ return get_field('images');
}
}
```
-**resources/views/archive.php**
+You can now include the Images trait into any view to pass on variable $images;
+
+**app/Controllers/Single.php**
```php
-@extends('layouts.app')
+<?php
-@section('content')
+namespace App\Controllers;
- @while (have_posts()) @php(the_post())
- {{ Archive::title() }}
- @endwhile
+use Sober\Controller\Controller;
-@endsection
+class Single extends Controller
+{
+ use Partials\Images;
+}
```
-#### Inheriting the Tree/Heirarchy;
+### Inheriting the Tree/Heirarchy;
By default, each Controller overrides its template heirarchy depending on the specificity of the Controller (the same way WordPress templates work).
You can inherit the data from less specific Controllers in the heirarchy by implementing the Tree.
-For example, the following `app/controllers/Single.php` example will inherit methods from `app/controllers/Singular.php`;
+For example, the following `app/Controllers/Single.php` example will inherit methods from `app/Controllers/Singular.php`;
-**app/controllers/Single.php**
+**app/Controllers/Single.php**
```php
<?php
-namespace App;
+namespace App\Controllers;
use Sober\Controller\Controller;
use Sober\Controller\Module\Tree;
```php
<?php
-namespace App;
+namespace App\Controllers;
use Sober\Controller\Controller;
}
```
-You can override a `app/controllers/Singular.php` method by declaring the same method name in `app/controllers/Single.php`;
+You can override a `app/Controllers/Singular.php` method by declaring the same method name in `app/Controllers/Single.php`;
-#### Creating Global Properties;
+### Creating Global Properties;
-Methods created in `app/controllers/App.php` will be inherited by all views and can not be disabled as `resources/views/layouts/app.php` extends all views.
+Methods created in `app/Controllers/App.php` will be inherited by all views and can not be disabled as `resources/views/layouts/app.php` extends all views.
-**app/controllers/App.php**
+**app/Controllers/App.php**
```php
<?php
-namespace App;
+namespace App\Controllers;
use Sober\Controller\Controller;
}
```
-#### Disable Option;
+### Advanced Custom Fields Module;
+
+Controller has an useful Advanced Custom Fields helper module to automate passing on fields.
+
+The automated fields will use the variable names from Advanced Custom Fields and pass them onto the view. Controller also passes on options values by default.
+
+```php
+<?php
+
+namespace App\Controllers;
+
+use Sober\Controller\Controller;
+
+class Single extends Controller
+{
+ // Pass on all fields from Advanced Custom Fields to the view
+ protected $acf = true;
+
+ // Pass on only field_1 from Advanced Custom Fields to the view
+ protected $acf = 'field_1';
+
+ // Pass on multiple fields from Advanced Custom Fields to the view
+ protected $acf = ['field_1', 'field_2'];
+}
+```
+
+The values are returned as objects, however you can disable this to keep them as arrays.
+
+```php
+add_filter('sober/controller/acf/array', function () {
+ return true;
+});
+```
+
+
+### Template Override Option;
+
+You should only use overrides in edge-case scenarios. Sticking to the WordPress hierarchy is recommended usage. However, one edge-case is the 404 template.
+
+In your Blade view, you would have `404.blade.php` as it begins with a number. In this case, you could rename your Controller class `FourZeroFour.php` and use parameter `$template = '404';`
+
+```php
+<?php
+
+namespace App\Controllers;
+
+use Sober\Controller\Controller;
+
+class FourZeroFour extends Controller
+{
+ protected $template = '404';
+}
+```
+
+### Lifecycles;
+
+Controller Classes come with two lifecycle hooks for greater control.
+
+```php
+public function __before()
+{
+ // runs after this->data is set up, but before the class methods are run
+}
+
+public function __after()
+{
+ // runs after all the class methods have run
+}
+```
+
+### Disable Option;
```php
protected $active = false;
```
-#### Blade Debugging;
+### Blade Debugger;
In your Blade views, `resources/views`, you can use the following to assist with debugging;
-* `@debug('hierarchy')` echos a list of the controller hierarchy for the current view.
-* `@debug('controller')` echos a list of variables available in the view.
-* `@debug('dump')` var_dumps a list of variables available in the view, including `$post`.
+* `@debug`
+* `@dump(__var__)`
-## Updates
+### Blade Coder;
+
+In your Blade views, `resources/views`, you can use the following to assist with jump-starting coding;
-#### Composer:
+* `@code`
+* `@code('__name of variable as string__')`
-* Change the composer.json version to ^9.0.0-beta3
+To wrap the code in if statements, use `@codeif`
+
+* `@codeif`
+* `@codeif('__name of variable as string__')`
+
+## Updates
+
+* Change the composer.json version to 2.1.0
* Check [CHANGELOG.md](CHANGELOG.md) for any breaking changes before updating.
```shell
$ composer update
```
-#### WordPress:
-
-Includes support for [github-updater](https://github.com/afragen/github-updater) to keep track on updates through the WordPress backend.
-* Download [github-updater](https://github.com/afragen/github-updater)
-* Clone [github-updater](https://github.com/afragen/github-updater) to your sites plugins/ folder
-* Activate via WordPress
-
-## Social
+## Other
-* For Controller updates and other WordPress dev, follow [@withjacoby](https://twitter.com/withjacoby)
+* For updates follow [@withjacoby](https://twitter.com/withjacoby)
+* You can also [hire me](mailto:darren@jacoby.co.za) for WordPress or frontend work
},
"require": {
"php": ">=5.6.0",
- "composer/installers": "~1.0",
- "hassankhan/config": "^0.10.0",
- "symfony/yaml": "^3.2",
+ "composer/installers": "^1.5",
"brain/hierarchy": "^2.3"
},
"require-dev": {
- "squizlabs/php_codesniffer": "2.*"
+ "squizlabs/php_codesniffer": "^3.2"
},
"autoload": {
"psr-4": {
- "Sober\\Controller\\": "src/",
- "Sober\\Controller\\Module\\": "src/Module/"
+ "Sober\\Controller\\": "src/"
},
"files": [
"controller.php"
},
"scripts": {
"test": [
- "vendor/bin/phpcs --extensions=php --ignore=vendor/ ."
+ "phpcs --extensions=php --ignore=vendor/ ."
]
}
}
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "30fe0043cc780fd4ab03227a788c074c",
+ "content-hash": "5e493d80c032a96e944afa3657f951d8",
"packages": [
{
"name": "brain/hierarchy",
- "version": "2.3.0",
+ "version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/Brain-WP/Hierarchy.git",
- "reference": "adb52da233e586051a577ef5eabe2c537df785d5"
+ "reference": "239f9bbe49ff6ad1199bbcad308d2c7977bee25a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Brain-WP/Hierarchy/zipball/adb52da233e586051a577ef5eabe2c537df785d5",
- "reference": "adb52da233e586051a577ef5eabe2c537df785d5",
+ "url": "https://api.github.com/repos/Brain-WP/Hierarchy/zipball/239f9bbe49ff6ad1199bbcad308d2c7977bee25a",
+ "reference": "239f9bbe49ff6ad1199bbcad308d2c7977bee25a",
"shasum": ""
},
"require": {
"keywords": [
"wordpress"
],
- "time": "2016-09-15T13:33:53+00:00"
+ "time": "2017-12-28T17:16:07+00:00"
},
{
"name": "composer/installers",
- "version": "v1.4.0",
+ "version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/composer/installers.git",
- "reference": "9ce17fb70e9a38dd8acff0636a29f5cf4d575c1b"
+ "reference": "049797d727261bf27f2690430d935067710049c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/installers/zipball/9ce17fb70e9a38dd8acff0636a29f5cf4d575c1b",
- "reference": "9ce17fb70e9a38dd8acff0636a29f5cf4d575c1b",
+ "url": "https://api.github.com/repos/composer/installers/zipball/049797d727261bf27f2690430d935067710049c2",
+ "reference": "049797d727261bf27f2690430d935067710049c2",
"shasum": ""
},
"require": {
},
"require-dev": {
"composer/composer": "1.0.*@dev",
- "phpunit/phpunit": "4.1.*"
+ "phpunit/phpunit": "^4.8.36"
},
"type": "composer-plugin",
"extra": {
"lavalite",
"lithium",
"magento",
+ "majima",
"mako",
"mediawiki",
"modulework",
+ "modx",
"moodle",
"osclass",
"phpbb",
"piwik",
"ppi",
"puppet",
+ "pxcms",
"reindex",
"roundcube",
"shopware",
"zend",
"zikula"
],
- "time": "2017-08-09T07:53:48+00:00"
- },
- {
- "name": "hassankhan/config",
- "version": "0.10.0",
- "source": {
- "type": "git",
- "url": "https://github.com/hassankhan/config.git",
- "reference": "06ac500348af033f1a2e44dc357ca86282626d4a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/hassankhan/config/zipball/06ac500348af033f1a2e44dc357ca86282626d4a",
- "reference": "06ac500348af033f1a2e44dc357ca86282626d4a",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.0"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.0",
- "scrutinizer/ocular": "~1.1",
- "squizlabs/php_codesniffer": "~2.2"
- },
- "suggest": {
- "symfony/yaml": "~2.5"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Noodlehaus\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Hassan Khan",
- "homepage": "http://hassankhan.me/",
- "role": "Developer"
- }
- ],
- "description": "Lightweight configuration file loader that supports PHP, INI, XML, JSON, and YAML files",
- "homepage": "http://hassankhan.me/config/",
- "keywords": [
- "config",
- "configuration",
- "ini",
- "json",
- "microphp",
- "unframework",
- "xml",
- "yaml",
- "yml"
- ],
- "time": "2016-02-11T16:21:17+00:00"
- },
- {
- "name": "symfony/yaml",
- "version": "v3.3.6",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/yaml.git",
- "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/ddc23324e6cfe066f3dd34a37ff494fa80b617ed",
- "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5.9"
- },
- "require-dev": {
- "symfony/console": "~2.8|~3.0"
- },
- "suggest": {
- "symfony/console": "For validating YAML files using the lint command"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.3-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Yaml\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony Yaml Component",
- "homepage": "https://symfony.com",
- "time": "2017-07-23T12:43:26+00:00"
+ "time": "2017-12-29T09:13:20+00:00"
}
],
"packages-dev": [
{
"name": "squizlabs/php_codesniffer",
- "version": "2.9.1",
+ "version": "3.3.1",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62"
+ "reference": "628a481780561150481a9ec74709092b9759b3ec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62",
- "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/628a481780561150481a9ec74709092b9759b3ec",
+ "reference": "628a481780561150481a9ec74709092b9759b3ec",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
- "php": ">=5.1.2"
+ "php": ">=5.4.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.0"
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
- "scripts/phpcs",
- "scripts/phpcbf"
+ "bin/phpcs",
+ "bin/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.x-dev"
+ "dev-master": "3.x-dev"
}
},
- "autoload": {
- "classmap": [
- "CodeSniffer.php",
- "CodeSniffer/CLI.php",
- "CodeSniffer/Exception.php",
- "CodeSniffer/File.php",
- "CodeSniffer/Fixer.php",
- "CodeSniffer/Report.php",
- "CodeSniffer/Reporting.php",
- "CodeSniffer/Sniff.php",
- "CodeSniffer/Tokens.php",
- "CodeSniffer/Reports/",
- "CodeSniffer/Tokenizers/",
- "CodeSniffer/DocGenerators/",
- "CodeSniffer/Standards/AbstractPatternSniff.php",
- "CodeSniffer/Standards/AbstractScopeSniff.php",
- "CodeSniffer/Standards/AbstractVariableSniff.php",
- "CodeSniffer/Standards/IncorrectPatternException.php",
- "CodeSniffer/Standards/Generic/Sniffs/",
- "CodeSniffer/Standards/MySource/Sniffs/",
- "CodeSniffer/Standards/PEAR/Sniffs/",
- "CodeSniffer/Standards/PSR1/Sniffs/",
- "CodeSniffer/Standards/PSR2/Sniffs/",
- "CodeSniffer/Standards/Squiz/Sniffs/",
- "CodeSniffer/Standards/Zend/Sniffs/"
- ]
- },
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
"phpcs",
"standards"
],
- "time": "2017-05-22T02:43:20+00:00"
+ "time": "2018-07-26T23:47:18+00:00"
}
],
"aliases": [],
namespace Sober\Controller;
+use Brain\Hierarchy\Hierarchy;
+
+use function App\sage;
+
/**
- * Functions
+ * Loader
*/
function loader()
{
- $loader = new Loader();
- foreach ($loader->getData() as $template => $class) {
- // Pass data filter
- add_filter('sage/template/' . $template . '-data/data', function ($data) use ($loader, $class) {
- $controller = new $class();
- $controller->__setup();
- return array_merge($loader->getAppData(), $loader->getPostData(), $controller->__setTreeData($data), $controller->__getData());
- });
- // Class alais
- class_alias($class, (new \ReflectionClass($class))->getShortName());
+ if (!function_exists('\App\sage')) {
+ return;
+ }
+
+ // Run WordPress hierarchy class
+ $hierarchy = new Hierarchy();
+
+ // Run Loader class and pass on WordPress hierarchy class
+ $loader = new Loader($hierarchy);
+
+ // Use the Sage DI container
+ $container = sage();
+
+ // Loop over each class
+ foreach ($loader->getClassesToRun() as $class) {
+ // Create the class on the DI container
+ $controller = $container->make($class);
+
+ // Set the params required for template param
+ $controller->__setParams();
+
+ // Determine template location to expose data
+ $location = "sage/template/{$controller->__getTemplateParam()}-data/data";
+
+ // Pass data to filter
+ add_filter($location, function ($data) use ($container, $class) {
+ // Recreate the class so that $post is included
+ $controller = $container->make($class);
+
+ // Params
+ $controller->__setParams();
+
+ // Lifecycle
+ $controller->__before();
+
+ // Data
+ $controller->__setData($data);
+
+ // Lifecycle
+ $controller->__after();
+
+ // Return
+ return $controller->__getData();
+ }, 10, 2);
}
}
-function debugger()
+/**
+ * Blade
+ */
+function blade()
{
- if (function_exists('\\App\\sage')) {
- \App\sage('blade')->compiler()->directive('debug', function ($type) {
- $debugger = ($type === '' ? '"controller"' : $type);
- return '<?php (new \Sober\Controller\Module\Debugger(get_defined_vars(), ' . $debugger . ')); ?>';
- });
+ if (!function_exists('\App\sage')) {
+ return;
}
+
+ // Debugger
+ sage('blade')->compiler()->directive('debug', function () {
+ return '<?php (new \Sober\Controller\Blade\Debugger(get_defined_vars())); ?>';
+ });
+
+ sage('blade')->compiler()->directive('dump', function ($param) {
+ return "<?php (new Illuminate\Support\Debug\Dumper)->dump({$param}); ?>";
+ });
+
+ // Coder
+ sage('blade')->compiler()->directive('code', function ($param) {
+ $param = ($param) ? $param : 'false';
+ return "<?php (new \Sober\Controller\Blade\Coder(get_defined_vars(), {$param})); ?>";
+ });
+
+ sage('blade')->compiler()->directive('codeif', function ($param) {
+ $param = ($param) ? $param : 'false';
+ return "<?php (new \Sober\Controller\Blade\Coder(get_defined_vars(), {$param}, true)); ?>";
+ });
}
/**
*/
if (function_exists('add_action')) {
add_action('init', __NAMESPACE__ . '\loader');
- add_action('init', __NAMESPACE__ . '\debugger');
+ add_action('init', __NAMESPACE__ . '\blade');
}
--- /dev/null
+<?php
+
+namespace Sober\Controller;
+
+class Blade
+{
+ protected $data;
+
+ /**
+ * Set Data
+ *
+ * Remove other array items should last item not include tree
+ */
+ protected function setBladeData($data)
+ {
+ // Get __blade/__debugger key
+ $this->data = $data['__data']['__blade'];
+
+ // Get first item from data array
+ $first = reset($this->data);
+
+ // Get last item from data array
+ $last = end($this->data);
+
+ // If last item does not inherit tree and first class is App
+ if (!$last->tree && $first->class === 'App') {
+ // Rewrite $this->data with first (App) and last item in array
+ $this->data = [$first, $last];
+ // Else if $last does not inherit tree
+ } elseif (!$last->tree) {
+ // Rewrite $this->data with last item in array
+ $this->data = $last;
+ }
+
+ return $this;
+ }
+}
--- /dev/null
+<?php
+
+namespace Sober\Controller\Blade;
+
+use Sober\Controller\Blade;
+use Sober\Controller\Utils;
+
+class Coder extends Blade
+{
+ private $code = '';
+ private $indentation = '';
+ private $includes;
+ private $codeif;
+
+ /**
+ * Construct
+ *
+ * Initialise the Code methods
+ */
+ public function __construct($data, $includes, $codeif = false)
+ {
+ // codeif
+ $this->codeif = $codeif;
+
+ // Set data from @code('var')
+ $this->setIncludeData($includes);
+
+ // Set data from $data['__data']['__blade']
+ $this->setBladeData($data);
+
+ // Render to view
+ $this->render();
+ }
+
+ private function setIncludeData($includes)
+ {
+ $this->includes = $includes;
+
+ if (is_string($this->includes)) {
+ $this->includes = [$this->includes];
+ }
+ }
+
+ /**
+ * Increase Indentation
+ *
+ * Add two spaces to $this->indentation
+ */
+ private function increaseIndentation()
+ {
+ $this->indentation = "{$this->indentation} ";
+ }
+
+ /**
+ * Decrease Indentation
+ *
+ * Remove two spaces from $this->indentation
+ */
+ private function decreaseIndentation()
+ {
+ $this->indentation = substr($this->indentation, 0, -2);
+ }
+
+ /**
+ * Render
+ *
+ * Loop through $this->data and echo code
+ */
+ private function render()
+ {
+ // Map the data results to exclude static methods
+ $this->data = array_map(function ($item) {
+ return $item->data;
+ }, $this->data);
+
+ // Remove the first level of the array so that we are left with flat variables
+ $this->data = call_user_func_array('array_merge', $this->data);
+
+ // Remove $post by default
+ unset($this->data['post']);
+
+ // Start @code block
+ $type = ($this->codeif ? '@codeif' : '@code');
+ echo "<pre class=\"coder\"><strong>{$type}</strong><br>";
+
+ // Run through each item
+ foreach ($this->data as $name => $value) {
+ // Remove the method/returned for data methods
+ $value = (isset($value->method) ? $value->returned : $value);
+
+ // Router
+ // @code('var')
+ if ($this->includes && in_array($name, $this->includes)) {
+ $this->router($name, $value);
+ }
+
+ // @code
+ if (!$this->includes) {
+ $this->router($name, $value);
+ }
+ }
+
+ // End @code block
+ echo '</pre>';
+ }
+
+ /**
+ * Router
+ *
+ * Route data types to correct methods
+ */
+ private function router($name, $val)
+ {
+ // Route object
+ if (is_object($val)) {
+ $this->renderObj($name, $val);
+ }
+
+ // Route indexed array
+ if (is_array($val) && Utils::isArrayIndexed($val)) {
+ $this->renderArrIndexed($name, $val);
+ }
+
+ // Route array with keys
+ if (is_array($val) && !Utils::isArrayIndexed($val)) {
+ $this->renderArrKeys($name, $val);
+ }
+
+ // Route strings/other
+ if (!is_array($val) && !is_object($val)) {
+ // Add to $this->code
+ $this->renderResult($name, $val);
+
+ // Clear out $this-code
+ $this->code = '';
+
+ // Exit
+ return;
+ }
+ }
+
+ /**
+ * Render Object
+ *
+ * Render an object
+ */
+ private function renderObj($name, $val)
+ {
+ // Get props of object
+ $props = get_object_vars($val);
+
+ // For each of those props
+ foreach ($props as $prop_name => $prop_val) {
+ // Add to $this->code
+ $this->code = "{$this->code}{$name}->";
+
+ // Route new values
+ $this->router($prop_name, $prop_val);
+ }
+ }
+
+ /**
+ * Render Indexed Array
+ *
+ * Render an indexed array
+ */
+ private function renderArrIndexed($name, $val)
+ {
+ if ($this->codeif) {
+ // Echo if
+ echo "{$this->indentation}@if (\${$this->code}$name)<br>";
+
+ // Increase indentation
+ $this->increaseIndentation();
+ }
+
+ // Start foreach
+ echo "{$this->indentation}@foreach (\${$this->code}$name as \$item)<br>";
+
+ // Clear $this->code
+ $this->code = '';
+
+ // Increase indentation
+ $this->increaseIndentation();
+
+ // Route next value
+ foreach ($val as $key_index => $key_val) {
+ if (count($val) > 1) {
+ echo "{$this->indentation}<strong>[{$key_index}]</strong><br>";
+ }
+ $this->router('item', $key_val);
+ }
+
+ // Decrease indentation
+ $this->decreaseIndentation();
+
+ // End foreach
+ echo "{$this->indentation}@endforeach<br>";
+
+ if ($this->codeif) {
+ // Decrease indentation
+ $this->decreaseIndentation();
+
+ // Echo endif
+ echo "{$this->indentation}@endif<br>";
+ }
+ }
+
+ /**
+ * Render Array Keys
+ *
+ * Render an array with keys
+ */
+ private function renderArrKeys($name, $val)
+ {
+ // Foreach value add key
+ foreach ($val as $key_name => $key_val) {
+ $this->router("{$this->code}{$name}['{$key_name}']", $key_val);
+ }
+ }
+
+ /**
+ * Render Result
+ *
+ * Render the final result
+ */
+ private function renderResult($name, $val)
+ {
+ $this->code = "\${$this->code}{$name}";
+
+ if ($this->codeif) {
+ // Echo if
+ echo "{$this->indentation}@if ({$this->code})<br>";
+
+ // Increase indentation
+ $this->increaseIndentation();
+ }
+
+ // Wrap with {{ }} or {!! !!}
+ if (Utils::doesStringContainMarkup($val)) {
+ $this->code = "{!! {$this->code} !!}";
+ } else {
+ $this->code = "{{ {$this->code} }}";
+ }
+
+ // Echo code
+ echo "{$this->indentation}{$this->code}<br>";
+
+ if ($this->codeif) {
+ // Increase indentation
+ $this->decreaseIndentation();
+
+ // Echo endif
+ echo "{$this->indentation}@endif<br>";
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace Sober\Controller\Blade;
+
+use Sober\Controller\Blade;
+
+class Debugger extends Blade
+{
+ private $controller;
+ private $controllerDataLog = [];
+
+ /**
+ * Construct
+ *
+ * Initialise the Debugger methods
+ */
+ public function __construct($data)
+ {
+ // Set data from $data['__data']['__blade']
+ $this->setBladeData($data);
+
+ // Render to view
+ $this->render();
+ }
+
+ /**
+ * Render
+ *
+ * Loop through $this->data and echo debugger information
+ */
+ private function render()
+ {
+ echo '
+ <style>
+ .debugger small {
+ border: 1px solid rgba(0,0,0,0.2);
+ opacity: 0.5;
+ padding: 2px 5px;
+ margin-left: 5px;
+ border-radius: 2px;
+ }
+ </style>';
+
+ echo '<pre class="debugger"><strong>@debug</strong><br>';
+
+ foreach ($this->data as $index => $controller) {
+ // Set the class params for each Controller
+ $this->controller = $controller;
+
+ // Echo the Controller class name
+ echo $controller->class;
+
+ // Echo extends tree if the Controller implements tree
+ if ($controller->tree) {
+ echo '<small>extends tree</small>';
+ }
+
+ echo '<ul>';
+
+ // Render data
+ if ($controller->data) {
+ $this->renderData();
+ }
+
+ // Render methods
+ if ($controller->methods) {
+ $this->renderMethods();
+ }
+
+ echo '</ul>';
+ }
+
+ echo '</pre>';
+ }
+
+ /**
+ * Render Data
+ *
+ * Render the current Controller item data
+ */
+ private function renderData()
+ {
+ echo '<li>Data<ul>';
+
+ foreach ($this->controller->data as $name => $data) {
+ // Remove previous overrides
+ $override = false;
+
+ // Search the data log array for this name
+ $key = array_search($name, array_column($this->controllerDataLog, 'name'));
+
+ // If the name exists in the data log array then get the class
+ if ($key !== false) {
+ $override = $this->controllerDataLog[$key]['class'];
+ }
+
+ // Update data log array with this items name and class
+ $this->controllerDataLog[] = [
+ 'name' => $name,
+ 'class' => $this->controller->class
+ ];
+
+ // Get data type
+ $dataType = (isset($data->method) ? gettype($data->returned) : gettype($data));
+
+ // Echo
+ echo "<li>{$name}";
+
+ // Override
+ if ($override) {
+ echo "<small>overrides {$override}</small>";
+ }
+
+ // Data type
+ echo "<small>{$dataType}</small>";
+
+ // Method lines
+ if (isset($data->method)) {
+ echo "<small>line {$data->method->getStartLine()}—{$data->method->getEndLine()}</small>";
+ }
+
+ echo '</li>';
+ }
+
+ echo '</ul></li>';
+ }
+
+ /**
+ * Render Methods
+ *
+ * Render the current Controller item methods
+ */
+ private function renderMethods()
+ {
+ echo '<li>Methods<ul>';
+
+ foreach ($this->controller->methods as $method) {
+ echo "<li>{$method->name}<small>line {$method->getStartLine()}—{$method->getEndLine()}</small></li>";
+ }
+
+ echo '</ul></li>';
+ }
+}
namespace Sober\Controller;
+use Sober\Controller\Utils;
+use Sober\Controller\Module\Acf;
+
class Controller
{
+ // Config
protected $active = true;
+ protected $template = false;
protected $tree = false;
+ protected $acf = false;
protected $data = [];
+ // Controller
private $class;
private $methods;
+ private $dataMethods;
+ private $staticMethods;
+
+ // Loader
+ private $incomingData;
+
+ // Deps
+ private $classAcf;
+
+ /**
+ * Before
+ *
+ * Lifecycle to interact with Controller after __setParams()
+ */
+ public function __before()
+ {
+ }
- public function __setup()
+ /**
+ * After
+ *
+ * Lifecycle to interact with Controller before __getData()
+ */
+ public function __after()
{
- $this->__setClass();
- $this->__setMethods();
- $this->__runMethods();
}
/**
- * Set Class
+ * Set Params
*
- * Create a ReflectionClass object for this instance
+ * Set the Controller template and tree params
*/
- private function __setClass()
+ final public function __setParams()
{
+ // $this->class
$this->class = new \ReflectionClass($this);
+
+ // $this->classAcf
+ if (class_exists('Acf')) {
+ $this->classAcf = new Acf();
+ }
+
+ // $this->template
+ if (!$this->template) {
+ $this->template = Utils::convertToKebabCase($this->class->getShortName());
+ }
+
+ // $this->tree
+ if ($this->class->implementsInterface('\Sober\Controller\Module\Tree')) {
+ $this->tree = true;
+ }
}
/**
- * Set Methods
+ * Set Controller Data
*
- * Set all Class public methods for this instance
+ * Set the Controller raw data for this Controller
+ * @return $this
*/
- private function __setMethods()
+ final public function __setData($incomingData)
{
- $this->methods = $this->class->getMethods(\ReflectionMethod::IS_PUBLIC);
+ $this->incomingData = $incomingData;
+
+ // Set the data from the WordPress post if singular to $this->data
+ $this->__setDataFromPost();
+
+ // Set the data from Advanced Custom Fields to $this->data
+ $this->__setDataFromModuleAcf();
+
+ // Set incoming filter data from Sage to App before Debugger
+ $this->__setDataFromFilter();
+
+ // Set the public methods from the class to $this->methods
+ $this->__setDataFromMethods();
+
+ // Set debugger data first to use only the raw data from the Controller
+ $this->__setBladeData();
+
+ // Set app data to $this->data['__app'] or merge with current data
+ $this->__setAppData();
+
+ // Set tree data to $this->data['__tree'] or merge with current data
+ $this->__setTreeData();
}
/**
- * Set Tree Data
+ * Set Data From Post
*
- * @return array
+ * Set the WordPress post
*/
- public function __setTreeData($data)
+ final private function __setDataFromPost()
{
- if (!$this->class->implementsInterface('\Sober\Controller\Module\Tree') && $this->tree === false) {
- $data = [];
+ // Only set data from $post to App class
+ if ($this->template !== 'app') {
+ return;
+ }
+
+ // Only continue if $post is available
+ if (!is_singular()) {
+ return;
}
- return $data;
+
+ // Set the post array to be included in $this->data
+ $this->data['post'] = get_post();
+ }
+
+ /**
+ * Set Data From Module Acf
+ *
+ * Set the Advanced Custom Fields data automatically
+ */
+ final private function __setDatafromModuleAcf()
+ {
+ // If $this->acf is not set then return
+ if (!$this->acf) {
+ return;
+ }
+
+ // Set the fields data passed in from Controller
+ $this->classAcf->setData($this->acf);
+
+ // Get the options page is $this->acf is set to true on App
+ if ($this->template === 'app' && is_bool($this->acf)) {
+ $this->classAcf->setDataOptionsPage();
+ }
+
+ // Deterime if acf/array filter is enabled and return correct format
+ $this->classAcf->setDataReturnFormat();
+
+ // Merge the data from Acf module
+ // $this->data = array_merge($this->classAcf->getData(), $this->data);
+ $this->data = array_merge($this->data, $this->classAcf->getData());
}
/**
- * Is Controller Method
+ * Set Sage Filter Data
*
- * Return true if the method belongs to the parent class
- * @return boolean
+ * Merge $this->data with $this->incomingData as Sage filters run before -data class filters
*/
- private function __isControllerMethod($method)
+ final private function __setDataFromFilter()
{
- $excls = get_class_methods(__CLASS__);
- $excls[] = '__construct';
- return (in_array($method->name, $excls));
+ if ($this->template === 'app') {
+ // Merge all incoming data from app to allow Sage add_filter support
+ $this->data = array_merge($this->data, $this->incomingData);
+ }
}
/**
- * Is Static Method
+ * Set Methods
*
- * Return true if the method is static
- * @return boolean
+ * Set $this->methods with all public methods
*/
- private function __isStaticMethod($method)
+ final private function __setDataFromMethods()
{
- $excls = [];
- $statics = $this->class->getMethods(\ReflectionMethod::IS_STATIC);
- foreach ($statics as $static) {
- $excls[] = $static->name;
+ // Get all public methods from class
+ $this->methods = $this->class->getMethods(\ReflectionMethod::IS_PUBLIC);
+
+ // Remove __contruct, __init, __finalize and this class methods from $this->methods
+ $this->methods = array_filter($this->methods, function ($method) {
+ return
+ $method->class !== 'Sober\Controller\Controller' &&
+ $method->name !== '__construct' &&
+ $method->name !== '__before' &&
+ $method->name !== '__after';
+ });
+
+ // Get all public static methods from class
+ $this->staticMethods = $this->class->getMethods(\ReflectionMethod::IS_STATIC);
+
+ // Remove $this->staticMethods from $this->methods using array_diff
+ $this->dataMethods = array_diff($this->methods, $this->staticMethods);
+
+ // Filter the remaining data methods
+ $this->dataMethods = array_filter($this->dataMethods, function ($method) {
+ return $method = $method->name;
+ });
+
+ // For each method convert method name to snake case and add to data[key => value]
+ foreach ($this->dataMethods as $method) {
+ // Convert method name to snake case
+ $var = Utils::convertToSnakeCase($method->name);
+
+ // Add var method name to data[]
+ $this->data[$var] = $this->{$method->name}();
}
- return (in_array($method->name, $excls));
}
/**
- * Sanitize Method
+ * Set Blade Data
*
- * Change method name from camel case to snake case
- * @return string
+ * Update $this->data with __blade
+ */
+ final private function __setBladeData()
+ {
+ // Get the data
+ $debuggerData = $this->data;
+
+ // Loop through each data method
+ foreach ($this->dataMethods as $dataMethod) {
+ // Convert the key to snake case to find in $debuggerData
+ $key = Utils::convertToSnakeCase($dataMethod->name);
+
+ // Save the returned value from the above key
+ $returned = $debuggerData[$key];
+
+ // Recreate the key with the method included
+ $debuggerData[$key] = (object) [
+ 'method' => $dataMethod,
+ 'returned' => $returned
+ ];
+ }
+
+ // Create the final debugger object
+ $debugger = (object) [
+ 'class' => $this->class->getShortName(),
+ 'tree' => $this->tree,
+ 'methods' => $this->staticMethods,
+ 'data' => $debuggerData
+ ];
+
+ // Include current debugger data in existing debugger array
+ $this->incomingData['__blade'][] = $debugger;
+
+ // Set the updated array to $this->data for @debug use
+ $this->data['__blade'] = $this->incomingData['__blade'];
+ }
+
+ /**
+ * Set App Data
+ *
+ * Update $this->data with __app
*/
- private function __sanitizeMethod($method)
+ final private function __setAppData()
{
- return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $method));
+ if ($this->template === 'app') {
+ // Save the app data in $this->data['__app']
+ $this->data['__app'] = $this->data;
+ // Esc the function
+ return;
+ }
+
+ // Save the app data to this $this->data['__app'] for next Controller
+ $this->data['__app'] = $this->incomingData['__app'];
+
+ // Include the app data with this current items data
+ $this->data = array_merge($this->data['__app'], $this->data);
}
/**
- * Run Methods
+ * Set Tree Data
*
- * Run and convert each of the child class public methods
+ * Update $this->data with tree data if required and store existing data
*/
- private function __runMethods()
+ final private function __setTreeData()
{
- foreach ($this->methods as $method) {
- if ($this->__isControllerMethod($method) || $this->__isStaticMethod($method)) {
- continue;
- }
- $this->data[$this->__sanitizeMethod($method->name)] = $this->{$method->name}();
+ if ($this->tree) {
+ // Include existing data with this Controller data
+ $this->data = array_merge($this->incomingData['__store'], $this->data);
}
+
+ // Save updated data to $this->data['__store'] for next Controller
+ $this->data['__store'] = array_merge($this->incomingData, $this->data);
+ }
+
+ /**
+ * Get Template Param
+ *
+ * @return string
+ */
+ final public function __getTemplateParam()
+ {
+ return ($this->active ? $this->template : false);
}
/**
- * Returns Data
+ * Get Controller Data
*
- * Set the class methods to be run
* @return array
*/
- public function __getData()
+ final public function __getData()
{
- return ($this->active ? $this->data : array());
+ return ($this->active ? $this->data : []);
}
}
namespace Sober\Controller;
+use Sober\Controller\Utils;
+use Brain\Hierarchy\Hierarchy;
+
class Loader
{
- protected $path;
- protected $files;
- protected $instance;
- protected $instances = [];
+ // Dep
+ private $hierarchy;
- public function __construct()
- {
- $this->setPath();
+ // User
+ private $namespace;
+ private $path;
- if (!file_exists($this->path)) {
- return;
- }
-
- $this->setDocumentClasses();
- $this->setFileList();
- $this->includeTraits();
- $this->includeClasses();
- }
+ // Internal
+ private $listOfFiles;
+ private $classesToRun = [];
/**
- * Set Path
+ * Construct
*
- * Set the default path or get the custom path
+ * Initialise the Loader methods
*/
- protected function setPath()
+ public function __construct(Hierarchy $hierarchy)
{
- $this->path = (has_filter('sober/controller/path') ? apply_filters('sober/controller/path', rtrim($this->path)) : dirname(get_template_directory()) . '/app/controllers');
- }
+ // Pass in WordPress hierarchy and set to param for reference
+ $this->hierarchy = $hierarchy;
- /**
- * Set File List
- *
- * Recursively get file list and place into array
- */
- protected function setFileList()
- {
- $this->files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->path));
- }
+ // Set the default or custom namespace used for Controller files
+ $this->setNamespace();
- /**
- * Set Classes to Body
- *
- * @return string
- */
- protected function setDocumentClasses()
- {
- add_filter('body_class', function ($body) {
- global $wp_query;
- $templates = (new \Brain\Hierarchy\Hierarchy())->getTemplates($wp_query);
- $templates = array_reverse($templates);
- $classes[] = 'app-data';
+ // Set the path using $this->namespace assuming PSR4 autoloading
+ $this->setPath();
- foreach ($templates as $template) {
- if (strpos($template, '.blade.php') || $template === 'index.php') {
- continue;
- }
- if ($template === 'index') {
- $template = 'index.php';
- }
- $classes[] = basename(str_replace('.php', '-data', $template));
- }
+ // Return if there are no Controller files
+ if (!file_exists($this->path)) {
+ return;
+ }
- return array_merge($body, $classes);
- });
+ // Set the list of files from the Controller files namespace/path
+ $this->setListOfFiles();
+
+ // Set the classes to run from the list of files
+ $this->setClassesToRun();
+
+ // Set the aliases for static functions from the list of classes to run
+ $this->setClassesAlias();
+
+ // Add the -data body classes for the Blade filter
+ $this->addBodyDataClasses();
}
/**
- * Set Instance
+ * Set Namespace
*
- * Add instance name and class to $instances[]
+ * Set the namespace from the filter or use the default
*/
- protected function setInstance()
+ protected function setNamespace()
{
- $class = get_declared_classes();
- $class = '\\' . end($class);
- $template = pathinfo($this->instance, PATHINFO_FILENAME);
- // Convert camel case to match template
- $template = strtolower(preg_replace('/(?<!^)[A-Z]/', '-$0', $template));
- $this->instances[$template] = $class;
+ $this->namespace =
+ (has_filter('sober/controller/namespace')
+ ? apply_filters('sober/controller/namespace', rtrim($this->namespace))
+ : 'App\Controllers');
}
/**
- * Is File
+ * Set Path
*
- * Determine if the file is a PHP file (excludes directories)
- * @return boolean
+ * Set the path assuming PSR4 autoloading from $this->namespace
*/
- protected function isFile()
+ protected function setPath()
{
- return (in_array(pathinfo($this->instance, PATHINFO_EXTENSION), ['php']));
+ $reflection = new \ReflectionClass($this->namespace .'\App');
+ $this->path = dirname($reflection->getFileName());
}
/**
- * Is File Class
+ * Set File List
*
- * Determine if the file is a Controller Class
- * @return boolean
+ * Recursively get file list and place into array
*/
- protected function isFileClass()
+ protected function setListOfFiles()
{
- return (strstr(file_get_contents($this->instance), "extends Controller") ? true : false);
+ $this->listOfFiles = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->path));
}
/**
- * Return Base Data
+ * Set Class Instances
*
- * @return array
+ * Load each Class instance and store in $instances[]
*/
- public function getAppData()
+ protected function setClassesToRun()
{
- if (array_key_exists('app', $this->instances)) {
- $app = new $this->instances['app']();
- $app->__setup();
- return $app->__getData();
+ foreach ($this->listOfFiles as $filename => $file) {
+ // Exclude non-PHP files
+ if (!Utils::isFilePhp($filename)) {
+ continue;
+ }
+
+ // Exclude non-Controller classes
+ if (!Utils::doesFileContain($filename, 'extends Controller')) {
+ continue;
+ }
+
+ // Set the classes to run
+ $this->classesToRun[] = $this->namespace . '\\' . pathinfo($filename, PATHINFO_FILENAME);
}
- return array();
}
/**
- * Return Post Data
+ * Set Class Alias
*
- * @return array
+ * Remove namespace from static functions
*/
- public function getPostData()
+ public function setClassesAlias()
{
- if (is_singular()) {
- return array('post' => get_post());
+ // Alias each class from $this->classesToRun
+ foreach ($this->classesToRun as $class) {
+ class_alias($class, (new \ReflectionClass($class))->getShortName());
}
- return array();
}
/**
- * Return Data
+ * Set Document Classes
*
+ * Set the classes required for the blade filter to pass on data
* @return array
*/
- public function getData()
+ protected function addBodyDataClasses()
{
- return $this->instances;
- }
+ add_filter('body_class', function ($body) {
+ global $wp_query;
+ // Get the template hierarchy from WordPress
+ $templates = $this->hierarchy->getTemplates($wp_query);
- /**
- * Traits Loader
- *
- * Load each Trait instance
- */
- protected function includeTraits()
- {
- foreach ($this->files as $filename => $file) {
- $this->instance = $filename;
- if (!$this->isFile() || $this->isFileClass()) {
- continue;
+ // Reverse the templates returned from $this->hierarchy
+ $templates = array_reverse($templates);
+
+ // Add app-data to classes array
+ $classes[] = 'app-data';
+
+ foreach ($templates as $template) {
+ // Exclude .blade.php and index.php
+ if (strpos($template, '.blade.php') || $template === 'index.php') {
+ continue;
+ }
+
+ // Exclude index as we use app
+ if ($template === 'index') {
+ $template = 'index.php';
+ }
+
+ // Replace .php with -data and add to the classes array
+ $classes[] = basename(str_replace('.php', '-data', $template));
}
- include_once $filename;
- }
+
+ // Return the new body class list for WordPress
+ return array_merge($body, $classes);
+ });
}
/**
- * Classes Loader
+ * Get Classes To Run
*
- * Load each Class instance
+ * @return array
*/
- protected function includeClasses()
+ public function getClassesToRun()
{
- foreach ($this->files as $filename => $file) {
- $this->instance = $filename;
- if (!$this->isFile() || !$this->isFileClass()) {
- continue;
- }
- include_once $filename;
- $this->setInstance();
- }
+ return $this->classesToRun;
}
}
--- /dev/null
+<?php
+
+namespace Sober\Controller\Module;
+
+class Acf
+{
+ // Config
+ protected $data = [];
+
+ private $returnArrayFormat = false;
+
+ /**
+ * Construct
+ *
+ * Initialise the Loader methods
+ */
+ public function __construct()
+ {
+ $this->setReturnFilter();
+ }
+
+ /**
+ * Set Return Filter
+ *
+ * Return filter sober/controller/acf-array
+ */
+ private function setReturnFilter()
+ {
+ $this->returnArrayFormat =
+ (has_filter('sober/controller/acf/array')
+ ? apply_filters('sober/controller/acf/array', $this->returnArrayFormat)
+ : false);
+ }
+
+ /**
+ * Set Data Return Format
+ *
+ * Return object from array if acf/array filter is not set to true
+ */
+ public function setDataReturnFormat()
+ {
+ if ($this->returnArrayFormat) {
+ return;
+ }
+
+ if ($this->data) {
+ foreach ($this->data as $key => $item) {
+ $this->data[$key] = json_decode(json_encode($item));
+ }
+ }
+ }
+
+ /**
+ * Set Data Options Page
+ *
+ * Set data from the options page
+ */
+ public function setDataOptionsPage()
+ {
+ if (!function_exists('acf_add_options_page')) {
+ return;
+ }
+
+ if (get_fields('options')) {
+ $this->data['acf_options'] = get_fields('options');
+ }
+ }
+
+ /**
+ * Set Data
+ *
+ * Set data from passed in field keys
+ */
+ public function setData($acf)
+ {
+ if (is_bool($acf)) {
+ $this->data = get_fields();
+ }
+
+ if (is_string($acf)) {
+ $this->data = [$acf => get_field($acf)];
+ }
+
+ if (is_array($acf)) {
+ foreach ($acf as $item) {
+ $this->data[$item] = get_field($item);
+ }
+ }
+ }
+
+ /**
+ * Get Data
+ *
+ * Return the data
+ * @return array
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+}
+++ /dev/null
-<?php
-
-namespace Sober\Controller\Module;
-
-class Debugger
-{
- protected $data;
-
- public function __construct($data, $type)
- {
- $this->data = $data;
- $this->type = $type;
-
- $this->sanitize();
- $this->route();
- }
-
- /**
- * Sanitize
- *
- * Remove __env, app and obLevel arrays from data
- */
- protected function sanitize()
- {
- $this->data = array_diff_key($this->data['__data'], array_flip(['__env', 'app', 'obLevel']));
- }
-
- /**
- * Route
- *
- * Run method depending on type
- */
- public function route()
- {
- if ($this->type === 'hierarchy') {
- $this->hierarchy();
- }
-
- if ($this->type === 'dump') {
- $this->dump();
- }
-
- if ($this->type === 'controller') {
- $this->controller();
- }
- }
-
- /**
- * Debug Dump
- *
- * Return var_dump of data
- */
- public function dump()
- {
- var_dump($this->data);
- }
-
- /**
- * Debug Controller
- *
- * Return list of keys from data
- */
- public function controller()
- {
- // unset($this->data['post']);
- echo '<pre><strong>Controller Debugger:</strong><ul>';
- foreach ($this->data as $name => $item) {
- $item = (is_array($item) ? gettype($item) . '[' . count($item) . ']' : gettype($item));
- echo "<li>$" . $name . " » " . $item . "</li>";
- }
- echo '</ul></pre>';
- }
-
- /**
- * Debug Hierarchy
- *
- * Return list of hierarchy
- */
- public function hierarchy()
- {
- global $wp_query;
- $templates = (new \Brain\Hierarchy\Hierarchy())->getTemplates($wp_query);
- $templates[] = 'app.php';
-
- $templates = array_map(function ($template) {
- if ($template === 'index') {
- $template = 'index.php';
- }
- if (strpos($template, '.blade.php')) {
- $template = str_replace('.blade', '', $template);
- }
- return basename($template);
- }, $templates);
-
- $templates = array_reverse(array_unique($templates));
-
- $path = get_stylesheet_directory() . '/controllers';
- $path = (has_filter('sober/controller/path') ? apply_filters('sober/controller/path', rtrim($path)) : dirname(get_template_directory()) . '/app/controllers');
- $path = basename($path);
-
- echo '<pre><strong>Hierarchy Debugger:</strong><ul>';
- foreach ($templates as $template) {
- echo "<li>" . $path . '/' . $template . "</li>";
- }
- echo '</ul></pre>';
- }
-}
+++ /dev/null
-<?php
-
-namespace Sober\Controller\Module;
-
-use Noodlehaus\Config;
-
-class Parser extends Config
-{
-
-}
--- /dev/null
+<?php
+
+namespace Sober\Controller;
+
+class Utils
+{
+ /**
+ * Is File PHP
+ *
+ * Determine if the file is a PHP file
+ * @return boolean
+ */
+ public static function isFilePhp($filename)
+ {
+ return (in_array(pathinfo($filename, PATHINFO_EXTENSION), ['php']));
+ }
+
+ /**
+ * Does File Contain
+ *
+ * Determine if the file contains a string
+ * @return boolean
+ */
+ public static function doesFileContain($filename, $str)
+ {
+ return strpos(file_get_contents($filename), $str) !== false;
+ }
+
+ /**
+ * Is Array Indexed
+ *
+ * Determine if the array is indexed
+ * @return boolean
+ */
+ public static function isArrayIndexed(array $array)
+ {
+ return array_keys($array) === range(0, count($array) - 1);
+ }
+
+ /**
+ * Does String Contain Markup
+ *
+ * Determine if the string contains markup
+ * @return boolean
+ */
+ public static function doesStringContainMarkup($str)
+ {
+ return (is_string($str) && $str !== strip_tags($str));
+ }
+
+ /**
+ * Convert To Snake Case
+ *
+ * Convert camel case to snake case for data variables
+ * @return string
+ */
+ public static function convertToSnakeCase($str)
+ {
+ return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $str));
+ }
+
+ /**
+ * Convert To Kebab Case
+ *
+ * Convert camel case to kebab case for templates
+ * @return string
+ */
+ public static function convertToKebabCase($str)
+ {
+ return strtolower(preg_replace('/(?<!^)[A-Z]/', '-$0', $str));
+ }
+}