Skip to content

Latest commit

 

History

History
696 lines (499 loc) · 23.3 KB

EASYDOCS.md

File metadata and controls

696 lines (499 loc) · 23.3 KB

EasyDocs for Jollof PHP Framework

This is a temporary location for accessing a very simple How to on using Jollof to develop apps.

Run-Through Examples

Below are some examples that can help you dig in in minutes.

Example 1 - Simple Page

Here, we will be build a simple page with Jollof.

Let's open the terminal or console window and create a view file by running the command below. This command will create an example folder with a start.view file inside it.

	$ php jollof make:view example/start

Move into the views folder (in the root) and into the example folder, open up the start.view file and add the markup below into the file.

                  <!DOCTYPE html>
                    <html>
                    	<head>
                      		<title> =@title </title>
                    	</head>
                    	<body>
                        	<h1>=@heading</h1>
            				[if:@heading == 'About Jollof']
            				<p>This is the caption</p>
            				[/if]
                        	<ul>
                        		[loop:@words]
                          			<li> [@words_value] </li>
                        		[/loop]
                        	</ul>
                    	</body>
                    </html>

Next, Let's create a simple GET route by running the command below. This command will add an entry into the setup.php file inside the routes folder.

    $ php jollof make:route /home GET

Let's now create a simple controller file by running the command below. This command will drop a Home.php file inside the controller folder.

NOTE: the name of the controller file MUST be the same as the first part of the route url.

    $ php jollof make:controller home

Move into the controllers folder (in the root), open up the Home.php file and add the code below into the file (within the index method).

  public function index($models){

      $words = array('Jollof', 'Make', 'Sense!');
      $heading = 'About Jollof';
      return Response::view('example/start', array(
                'title' => 'Example',
                'words' => $words,
                'heading' => $heading
      ));
  }

Next, move into the configs folder (in the root), open up the env.php file and add the last line to the app_auth config section (array).

        "app_auth" => array(
                .
                .
                .

                'guest_routes' => array( # These routes can be accessed only if the user is not logged in (guest).
                     '/',
                     '/account/login/',
                     '/account/register/',
                     '/account/signup/@mode/',
                     '/account/signin/@provider/',
                     '/home'
                )
        ),
        
        .
        .
        .        

Finally, move to the browser and load the route /home to view the page or view.

Example 2 - Content-Security-Policy (CSP) Activation

Move into the configs folder (in the root), open up the env.php file and edit the settings under app_security config section (array) to what you have below.

  .
  .
  .

  "app_security" => array(

            'strict_mode' => FALSE, #options: (FALSE, TRUE) ;

            'csp' => TRUE, // #options: (FALSE, TRUE, array(...)) ; Content-Security-Policy

            'hpkp' => FALSE, // #options: (FALSE, TRUE, array(...))

            'cspro' => FALSE, // #options: (FALSE, TRUE, array(...)) ;Content-Security-Policy-Reporting-Only:

            'noncify-inline-source' => TRUE // Generates a nonce value for each <script> and <style> tag code in your views

  )

  .
  .

Next, (still in the configs folder - in the root), move down the env.php file and add the last line to the app_auth config section (array).

        "app_auth" => array(
                .
                .
                .

                'guest_routes' => array( # These routes can be accessed only if the user is not logged in (guest).
                     '/',
                     '/account/login/',
                     '/account/register/',
                     '/account/signup/@mode/',
                     '/account/signin/@provider/',
                     '/home'
                )
        ),
        
        .
        .
        .        

In the view created in the first example (example/start), add an inline script tag to the head as below:

<head>
 .
 .
 .

<script type="text/javascript">
     var t = 'Jollof PHP';
     console.log(t);
</script>
</head>

Finally, serve the view in the browser as before using '/home'. Check the view source from the browser and notice CSP 'nonce=' values attached to the inline script tag.

Example 3 - Register and use a GIT webhook for pushing new code to your hosted website

Assuming you have uploaded a small website built with Jollof (see Example 1) to a host provider (e.g Digital Ocean, Hostgator, Bluehost, WhoGoHost) , go to the GitHub repository dashboard for your/the small website (under the Settings tab) and click Webhooks. Then, enter the below URL into the webhook endpoint (make sure you have SSL setup).

Choose a domain e.g [https://www.example.com]

https://www.exapmle.com/webhook/git-payload/{gitaccountname}/{gitprojectname}

Save all changes to the GitHub Settings tab and you are good to go.

Example 4 - Creating a user

Using the route [/account/signup/@mode/] which has already been setup in the routes [setip.php] file and also the todo-app.sql file in the root, follow the steps below:

  • Run the following command (take note of the database name you entered into the cli)
    $ php jollof make:env
  • Create a MySQL database and name it the same as in the command above

  • Import the todo-app.sql file into the already created database

  • Open up the Account controller file and edit the signup method like so

  public function signup(){

            $inputs = Request::input()->getFields(); /* get POST params */

            $validInputs = Validator::check( $inputs, /* validate POST params */
                array(
                    'email' => "email|required",
                    'password' => "password|required|bounds:8",
                    'first_name' => 'name|required',
                    'last_name' => 'name|required',
                    'mobile_number' => 'mobile_number|required'
                )
            );

            $json = array('status' => 'ok', 'result' => NULL); /* setting up resposne JSON */

            switch($this->params['mode']) {
                 case 'create':
                     if(Validator::hasErrors()){
                          $json['status'] = 'error';
                          $json['result'] = Validator::getErrors();
                     }else{

                        $json['result'] = Auth::register( $validInputs );
                     }
                 break;
            }

            if($json['status'] == 'ok'){
                if(isset($validInputs['auto-login'])
                   && $validInputs['auto-login'] === 'true'){
                        unset($validInputs['password']);
                        /* automatically log the user in */
                        Auth::auto($json['result'], $validInputs);
                }
            }    

            // $json['result'] = array();

            /* redirect to another route */
            return Response::redirect( '/admin');
  }
  • Also, edit the signin method like so
    public function signin(){

            $inputs = Request::input()->getFields();

            $validInputs = Validator::check( $inputs,
                array(
                    'email' => "email|required",
                    'password' => "password|required"
                )
            );

            $json = array( 'status' => 'ok', 'result' => NULL );

            switch($this->params['provider']) {
                 case 'oauth-facebook':
                     # code...
                 break;
                 case 'oauth-instagram':
                     # code...
                 break;
                 case 'email':
                    if(Validator::hasErrors()){
                        $json['status'] = 'error';
                        $json['result'] = Validator::getErrors();
                    }else{

                        $json['result'] = Auth::login( $validInputs );
                    }
                 break;
             }

             return Response::json( $json );

    }
  • Okay now, edit the logout method like so
  public function logout(){

        Auth::logout();

        // return Response::redirectBack();

        return Response::view('index', array('framework' => 'Jollof', 'title' => 'PHP MVC Framework'));
  }
  • Finally, serve the register view in the browser as before using '/account/register'

Example 5 - Data source read(s) and write(s)

Using the admin/index view in the views folder, open it up in a text editor or IDE and edit as below:

    <!DOCTYPE html>
    <html data-x-path="[!asset(/offline.html)]" lang="en">
    <head>
        <title>=@framework &dash; =@title</title>
        <meta charset="utf-8">
        <meta name="csrftoken" content="=@csrftoken">

        <link rel="icon" type="text/x-icon" href="[!asset(/favicon.ico)]">

        <link rel="manifest" type="application/manifest+json" href="[!asset(/manifest.json)]">
        <link rel="stylesheet" type="text/css" href="[!asset(/css/bootstrap.min.css)]">
        <link rel="stylesheet" type="text/css" href="[!asset(/css/bootstrap-theme.min.css)]">

        <style type="text/css">

                /**
                 *  NORMALIZE CSS 
                 */

                article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display:block; }
                audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; }
                audio:not([controls]), [hidden] { display:none; }

                /** Base Styles **/

                html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
                body { margin: 0; font-size: 13px; line-height: 1.5; }
                body, button, input, select, textarea { font-family: sans-serif; color: #000; }

                /** IE Fixes **/

                img { border: 0; -ms-interpolation-mode: bicubic; }
                svg:not(:root) { overflow: hidden; }
                figure { margin: 0; }

                /** Links **/
                a:focus { outline: thin dotted; }
                a:hover, a:active { outline: 0; }

                /** Typography **/
                h1 { font-size: 2em; } /* fixes html5 bug */
                p { -webkit-hyphens: auto; -moz-hyphens: auto; -epub-hyphens: auto; hyphens: auto; }
                abbr[title] { border-bottom: 1px dotted; }
                b, strong, .strong { font-weight: bold; }
                dfn, em, .em { font-style: italic; }
                small, .small, sub, sup { font-size: 75%; }
                ins, .ins { background: #ff9; color: #000; text-decoration: none; }
                mark, .mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; }
                hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
                pre, code, kbd, samp { font-family: monospace, serif; _font-family: 'courier new', monospace; font-size: 1em; }
                pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; }
                blockquote { margin: 1.5em 40px; }
                q { quotes: none; }
                q:before, q:after { content: ''; content: none; }
                ul, ol { margin: 1.5em 0; padding: 0; }
                dd { margin: 0; }
                nav ul, nav ol, .widget ol, .widget ul, .commentlist { list-style: none; list-style-image: none; margin: 0; }

                /* Position subscript and superscript content without affecting line-height: gist.github.com/413930 */
                sub, sup { line-height: 0; position: relative; vertical-align: baseline; }
                sup { top: -0.5em; }
                sub { bottom: -0.25em; bottom:1ex; }

                /** Forms **/

                form, fieldset, form ul, form ol, fieldset ol, fieldset ul { margin: 0; border: 0; }
                legend { border: 0; *margin-left: -7px; }
                button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; }
                button, input { line-height: normal; }
                button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; *overflow: visible; }
                input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; }
                input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; }
                input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
                button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
                textarea { overflow: auto; vertical-align: top; }
                textarea:focus, textarea:active { outline:none; outline:0;  }

                /* Colors for form validity */
                input:invalid, textarea:invalid { background-color: #f0dddd; }

                /** Tables **/
                table { border-collapse: collapse; border-spacing: 0; }

                .clear-fx:before,
                .clear-fx:after{
                    content: "\0020";
                    height:0;
                    display:block;
                    overflow: hidden;
                    visibility:hidden;
                    line-height:0;
                }

                .clear-fx:after {
                    clear: both;
                }

                *+html .clear-fx { 
                    /* IE less than 8 (6/7) */
                    zoom: 1;
                } 

                html, body{
                    margin:0;
                    padding:0;
                    border:none;
                    min-width:100%;
                    /* using IE star hack for fallback */
                    *width:100%;
                    height:100%;
                    overflow:hidden !important;
                    position:relative !important;
                }

                .row-box{
                    padding:0 10px;
                }

                .main-row{
                    position:relative;
                    width:auto !important;
                    padding-left:0;
                    padding-right:0;
                    height:100%;
                }

                .aside-col{
                    float:left;
                    position:relative !important;
                    width:180px;
                    height:100%;
                } 

                .main-col{
                    position:relative !important;
                    width:auto !important;
                    overflow:hidden !important;
                    height:100%;
                    background-color:#f5f5f5;
                    color:#888888;
                    padding-left:10px;
                }

                .x-left-col{
                    background-color:#fefefe;
                    color:#d5d5d5;
                }

                .x-right-col{
                    background-color:#3cefa1;
                    color:#ffffff;
                }
        </style>
    </head>
    <body>

        <div class="row-box main-row clear-fx">
            <aside class="aside-col x-left-col">

            </aside>
            <main class="main-col x-right-col">
                <ul class="">
                    [loop:@user]
                        <li>
                            <label>
                                [@user_index]
                            </label>
                            <span>
                                [@user_value]
                            </span>
                        </li>
                    [/loop]
                </ul>
            </main>
        </div>

        <script type="text/javascript" src="[!asset(/js/browsengine.js)]"></script>
        <script type="text/javascript" src="[!asset(/js/manup.js)]"></script>
        <script type="text/javascript" src="[!asset(/js/jquery-1.10.2.js)]"></script>
        <script type="text/javascript" src="[!asset(/js/bootstrap.js)]"></script>
    </body>
    </html>

Open up the Admin controller from the controllers folder and edit as follows:

    public function index(){

        $user = Auth::user(); /* user from session - logged in */

        if(!is_array($user)){

            $user = array();
        }

        return Response::view('admin/index', array('user' => $user, 'framework' => 'Jollof', 'title' => 'PHP MVC Framework'));
    }

Next, create a route and a controller together (at the same time) by running the below commands:

    $ php jollof make:route /tasks GET --controller
    $ php jollof make:route /tasks/create/@type POST

Then, open up the Tasks controller from the controllers folder and edit the index file as follows:

    public function index(){

        $user = Auth::user();
        
        $resultset = TodoList::fetchWith(Todo::class, array(
                            'user_id' => array('=', $user['id']), 
                            'project_id' => array('=', '45a2cd23f08bbd6477d2ff89715cba32de')
                        )
        );

        return Response::json(array(
                            'status' => 'ok', 
                            'result' = array(
                                'todos' => $resultset
                            )
                )
        ); 
    }

    public function create(){

         $input = Request::input()->getFields(); /* get post params - 'name' */

         $type = $this->params['type'];

         $user = Auth::user(); /* user from session */

         if(!is_array($user)){

                return Response::json(array(
                                            'status' => 'error', 
                                            'result' => 'not logged in'
                                        )
                        );
         }

         $project = Project::whereBy(array(
                                    'name' => array(
                                            '=', 
                                            'personal'
                                        ),
                                    )
                                    array('id', 'mode')
                    );

         $list = NULL;

         switch($type){
            case "list": /* for route -> 'tasks/create/list' */

                $list = TodoList::create(array(
                                    'name' => $input['name'], 
                                    'user_id' => $user['id']
                                )
                        );
            break;
         }

         return Response::json(array('status' => 'ok', 'result' => $list));

    }

Afterwards, open up the setup.php file in the routes folder (at the bottom of the file)...

    Router::bind('/tasks', array('inject' => array(), 'params' => array()));

    Router::bind('/tasks/create/@type', array('inject' => array(), 'params' => array('type' => '/^list$/'), 'verb' => 'post'));

Fianlly, move in the configs folder (in the root), scroll down the env.php file and add the last 3 lines to the app_auth config section (array).

        "app_auth" => array(
                .
                .
                .

                'guest_routes' => array( # These routes can be accessed only if the user is not logged in (guest).
                     '/',
                     '/account/login/',
                     '/account/register/',
                     '/account/signup/@mode/',
                     '/account/signin/@provider/',
                     '/home',
                     '/tasks',
                     '/tasks/create/@type/',
                )
        ),
        
        .
        .
        .        
         $resultset = TodoList::fetchWithOrder(Todo::class, array('user_id' => array('=', '1e253fc4672bcdd61369aab8c1534bd1f08c'), 'project_id' => array('=', '45a2cd23f08bbd6477d2ff89715cba32de')), array('created_at'));   
            
            /* The above code will retrieve all [todos] created by a given user (logged user) for a given project (logged user) */

            /* SELECT * FROM `tbl_todos_list` LEFT JOIN `tbl_todos` ON `tbl_todos_list`.`id` = `tbl_todos`.`list_id` WHERE `user_id` = '1e253fc4672bcdd61369aab8c1534bd1f08c' AND `project_id` = '45a2cd23f08bbd6477d2ff89715cba32de' ORDER BY `created_at` ASC */



         $resultset = User::fetchWith(TodoList::class, array('email' => '[email protected]'));
            
            /* The above code fetches the list of all [todo-lists] for all projects created by a given user (logged user - email) */

            /* SELECT * FROM `tbl_users` LEFT JOIN `tbl_todos_list` ON `tbl_users`.`id` = `tbl_todos_list`.`user_id` WHERE `email` = '[email protected]' */


         $wheres = array_pluck($resultset, 'project_id', 'id');

         $resultset = Project::whereByOr(array_flatten($wheres), array('id', 'name'));

Example 6 - Setting up CORS (Cross Origin Resource Sharing)

Move into the configs folder (in the root), open up the env.php file and edit the settings under app_auth config section (array) to what you have below.

This is just an example setup for CORS.

        .
        .
        .
        .
        .

        'cors' => array(
                
                    'credentials_pass' => TRUE, // enable recieving cookies from CORS request
                    'max_age' => 86400,
                    'exposed_headers' => array(
                        'Set-Cookie' // exposing the set-cookie header from CORS response
                    ),
                    /* HTTP methods must be listed in uppercase */
                    'allowed_methods' => array(
                        'GET',
                        'POST',
                        'PUT',
                        'DELETE'
                    ),
                    'allowed_headers' => array(
                         'X-Requested-With',
                         'X-Document-Hash' // custom header to allow from CORS request
                    ),
                    'allowed_origins' => array(
                        'https://myapp.example.com' // allow CORS request from this origin
                    )
        )