Coding a PHP 7 Framework #9 - Code Workshop

In part 9 of Coding a PHP framework, we explore how to achieve some more advanced functionality using the classes and steps we've already learnt.



Coding a PHP 7 Framework #9 - Code Workshop

18th October 2018 in   PHP Tutorials by Elliott Barratt

You can view the finished framework at any time by visiting the GitHub repo at https://github.com/erbarratt/lectric.

If you need more space to see the code, click "Hide Sidebar >" above the Article Categories box to the right ->

Now that we have seen how easy it is to deploy a pretty-url website using just a switch statement inside the /view/public/render.php file, let's build on what we already have from the last article to encorporate some more advanced features using /do/ actions and responses.

Application Configuration

First we'll need to add some definitions to our application specific /engine/app_config.php file, namely the database access details:

<?php
		
	/*Error Reporting on, turn off after deployment. */
		define('DEBUG', TRUE);
		
	/* 
	* core definitions
	*/
		define('DEFAULT_DIRECTORY', 'public');
		define('SITE_NAME','SmallApp');
		define('SITE_LINK','smallapp.com');
		define('SITE_DESCRIPTION','A small app built on the lectric PHP framework.');

	/* 
	* database access
	*/
		define('DB_NAME', 'live_db');
		define('DB_USER', 'live_user');
		define('DB_PASSWORD', 'livepass');
		define('DB_HOST', 'localhost');

These four constants allow us to connect to our database automatically in /engine/config.php. Speaking of which:

Database

If you haven't already, go back to part 1 of this article series, and run the sql query that will set up the public_views and public_directories tables. Our view class assumes any _directories table has at least a `name` field, and the _views table has at least a `url` field for us to be able to query against using our URL_NODES. Beyond those mandatory fields, those tables can contain any fields you'd like, we've just provided some common ones for our example.

Render

The render file will need ammending to make use of our new database connection when selecting a page:

<?php
	/*
	* load up webpage based on PAGE_URL 
	*/
		$this->page = $this->loadPage();

	/*
	* Include each template part from directory. Add switch here to facilitate other directories.
	*/
		include(DOC_ROOT.'/view/'.$this->_fileDirectory.'/template/common/header.php');
		include(DOC_ROOT.'/view/'.$this->_fileDirectory.'/template/common/content.php');
		include(DOC_ROOT.'/view/'.$this->_fileDirectory.'/template/common/footer.php'); 

Now we have swapped out the large switch statement in favour of using the loadPage() function in the /library/Lectric/view.class.php file, using $this since we're in the scope of that class. That function takes the current URL_NODES constant array to select a page from the database. If it's not found, it will attempt to load an error page from the same table and set the header to 404 not found.

Replacing $pageArray

Now that we have stored our page details into the property $page, we'll need to find and replace any use of $pageArray from our previous small application.

Our /view/public/template/common/header.php file:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8" />
	
    <title><?php echo $this->page['meta_title'].' | '.SITE_NAME; ?></title>
    <!--content meta information-->
    <meta name="description" content="<?php echo $this->page['meta_description'];?>" />
    <meta name="keywords" content="<?php echo $this->page['meta_keywords'];?>" />
    
</head>
<body>

And also our /view/public/template/views/index.php and /view/public/template/views/another-page.php files:

<?php 
    echo $this->page['html'];

Excellent, now our application uses the database to drive which view is served back to the user.

A Simple Contact Form

Now let us take a look at how we can use a /do/ action to accept input from a form and send that information to a given email address. First off, we need to insert a row into our public_views table:

'INSERT INTO `public_views` (`url`, `pageheading`, `metatitle`,`metadescription`,metakeywords`,`directory`,`live`) VALUES ("contact", "Contact Us", "Contact Us", "Send us a message", "contact,simple,application", 1, 1)'

This has created a contact us view, which can be found using the request URL http://random.site/contact. It has been put into the root directory (1, see `public_directories` table) and made live (1).

Now we can make a specific view template file for it, making /view/public/template/views/contact.php (which will get included in /view/public/template/common/content.php like in the last article):

<?php

	if (($lecMessages = \Lectric\controller::getSessionMessages()) !== null){
		
		?><div><?php
			foreach ($lecMessages as $msg){
				echo $msg.'<br/>';
			}	
			\Lectric\controller::clearSessionMessages();
		?></div><?php
		
	}

?>

<form method="post" action="/do/action/SmallApp/contact/sendEmail/">

    <input type="text" name="name" value="" placeholder="Your Name" />

    <input type="text" name="message" value="" placeholder="Your Message" />

    <button type="submit">Send</button>

</form>

It shouldn't need repeating, but this is just a simple example - you should implement client AND server-side validation / sanitisation whenever accepting input from an unknown source. For the purposes of these tutorials, client side validation has been omitted.

First we check for the presence of any session messages, and output them. We'll use this to notify the user when the message has been sent.

You'll see the "action" attribute on the form has been set to look for a contact class in the SmallApp namespace (see part 4 of this series for an explanation of how the doAction class works). Let's create this simple class now:

<?php
namespace SmallApp;

class contact
{

	public function do_sendEmail() :\Lectric\controllAction()
	{
		
		$message = htmlentities($_POST['message']);
		$subject = htmlentities('Contact from '.$_POST['name']);
		$to      = 'nobody@example.com';
		$headers = 'From: webmaster@example.com' . "\r\n" .
		    'Reply-To: webmaster@example.com' . "\r\n" .
		    'X-Mailer: PHP/' . phpversion();

		mail($to, $subject, $message, $headers);

		return new \Lectric\controlAction('view', '/contact/', 'Thanks for sending us a message!');
		
	}

}

The important thing to note, is that the function is prepended by do_ and it returns a \Lectric\controlAction() object. This object tells the controller (after performing a redirect) to go back to the /contact/ page and set a message thanking the user for filling out the form (this is caught in the /view/public/template/views/contact.php file we made earlier). 

And that's it! Now our small application can accept intput from a form, send an email and display a thank you message afterwards. Tasty.

Sitemap

A good example of where we may want a none-view /do/ response is a sitemap for our small application to use in something like Google Search Console. We want to respond with some sort of data, but we don't want to return HTML. All we need to do to implement this, is to add the file /do/sitemap/sitemap.php:

<?php

//we are in the doResponse class scope, hence $this for database queries.

//set header to xml
header("Content-Type: text/xml; charset=UTF-8");

?><?xml version="1.0"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
	
	<url><loc><?php echo SITE_LINK; ?></loc></url>
	
	<?php

	try{
		//public views
		$this->setWhereFields([
			'live'=>1
			'url'=>'error'
		]);
		$this->setWhereOps('=N');
		$webpages = $this->selStrict('public_views', \Lectric\lecPDO::MULTI);
	} catch (\Exception $e){
		if(DEBUG){
			echo 'Error loading public_views: '.$e->getMessage();
		}
	}

	if ($webpages != null){
		
		foreach ($webpages as $p){
			
			?><url><loc><?php echo SITE_LINK.'/'.$p['url'].'/'; ?></loc></url><?php
			
		}
		
	}

	?>

</urlset>

We can use $this in this file because we are in the scope of the doResponse class, which extends the lecPDO class. We start by setting our header as xml, then select all webpages that are live, except where the `url` field is 'error'. This is output as a urlset by looping over the results.

Hopefully it is evident how this same system could be used to implement an API. Maybe create /do/myApp/api.php and have it accept/parse and return some JSON? Or you could implement some ajax requests in the same manner. In all cases we want to return none-view type responses independant of the main view logic route.

Conclusion

That's how easy it is to start adding more complex functionality to an application, be it accepting input, or outputting none html responses. The Lectric framework allows you to concentrate on developing those sections, rather than reinventing the wheel every time you'd like to be able to parse a URL into a request.




Hide Sidebar >



Archive



Search


Hide Sidebar >


This website uses cookies. Privacy Policy