Skip to content

Instantly share code, notes, and snippets.

@ace411
Last active May 22, 2017 12:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ace411/e08e037eeac39d8bc0e5432e8ba15926 to your computer and use it in GitHub Desktop.
Save ace411/e08e037eeac39d8bc0e5432e8ba15926 to your computer and use it in GitHub Desktop.
Using the fauxton client with Bingo MVC and ReactJS
<!doctype HTML>
<html>
<head>
<meta charset="utf-8">
<title>{{ title }}</title>
<link rel="stylesheet" href="{{ stylesheet }}" type="text/css">
</head>
<body>
<h1 align="center">{{ title }}</h1>
<section class="entry-detail-container" id="entry-details"></section>
<section class="entry-form-container" id="entry-form"></section>
<script src="https://unpkg.com/react@15/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
<script src="{{ script }}"></script>
</body>
</html>
<?php
/**
* Controller for the CouchDB-React-Bingo project
*
* @package The Bingo Framework
* @author Lochemem Bruno Michael
*/
namespace App\Controllers;
class Basketball
{
use \Core\Controller;
/**
* Renders the basketball/index page
*/
public function indexAction()
{
echo $this->container['Views']->mustacheRender('basketball', [
'title' => 'NBA Player Statistics',
'script' => '/js/react-main.js',
'stylesheet' => '/css/main.css'
]);
}
/**
* Render the basketball/add-data page
* This action is built to interact with the data entry form on the index page via AJAX
*/
public function addDataAction()
{
$input = file_get_contents('php://input'); //get the input from the user
$input = json_decode($input, true);
print_r($this->container['CouchDb']->createDoc($input));
}
/**
* Render the basketball/get-data page
* This action is fashioned to get all the document data from the CouchDB database
*/
public function getDataAction()
{
print_r($this->container['CouchDb']->getAllDocs());
}
}
{
"name": "chemem/bingo-framework",
"description": "A simple PHP MVC framework",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Lochemem Bruno Michael",
"email": "lochbm@gmail.com"
}
],
"require": {
"php": ">=5.6.0",
"chemem/fauxton-client": "dev-master"
},
"require-dev": {
"mustache/mustache": "*",
"phpunit/phpunit": "*",
"michelf/php-markdown": "*",
"kriswallsmith/assetic": "*",
"pimple/pimple": "*"
},
"suggest": {
"chemem/fauxton-client": "Helps with CouchDB NoSQL interactions",
"leafo/lessphp": "Compile LESS files to CSS without using third-party compilers",
"leafo/scssphp": "Compile SCSS files to CSS without using third-party compilers",
"patchwork/jsqueeze": "Minify JS files",
"naxtet/CssMin": "Minify CSS files"
},
"scripts": {
"bundle-all": "@php cli/bingo-cli.php -ball",
"bundle-js": "@php cli/bingo-cli.php -bjs",
"bundle-less": "@php cli/bingo-cli.php -bless",
"bundle-css": "@php cli/bingo-cli.php -bcss",
"bundle-scss": "@php cli/bingo-cli.php -bscss",
"split-js": "@php cli/bingo-cli.php -spjs",
"split-css": "@php cli/bingo-cli.php -spcss",
"split-scss": "@php cli/bingo-cli.php -spscss",
"split-less": "@php cli/bingo-cli.php -spless",
"filter-js": "@php cli/bingo-cli.php -fjs",
"filter-less": "@php cli/bingo-cli.php -fless",
"filter-css": "@php cli/bingo-cli.php -fcss",
"filter-scss": "@php cli/bingo-cli.php -fscss"
},
"config": {
"prepend-autoloader": false,
"vendor-dir": "packages"
},
"autoload": {
"psr-4": {
"Core\\": "Core/",
"App\\": "App/"
}
}
}
<?php
/**
* Configuration options for the Bingo Framework
*
* @package Bingo Framework
* @author Lochemem Bruno Michael
*
*/
namespace App;
class Config
{
/**
* Database user-name parameter
*
* @var string DB_USER
*
*/
const DB_USER = '<couchdb-username>';
/**
* Database database host parameter
*
* @var string DB_HOST
*
*/
const DB_HOST = 'localhost';
/**
* Database user-password parameter
*
* @var string DB_PASS
*
*/
const DB_PASS = '<couchdb-password>';
/**
* Database name parameter
*
* @var string DB_NAME
*
*/
const DB_NAME = 'nba_info';
/**
* Show errors or convert them into readable logs
* Set to false in production
*
* @var bool SHOW_ERRORS
*
*/
const SHOW_ERRORS = true;
/**
* Set the error type for the configuration
* Options are: json, text-html
*
* @var string ERROR_TYPE
*
*/
const ERROR_TYPE = 'text-html';
/**
* Dependency directory
* @see composer.json for the name of the root dependency folder
*
* @var string DEP_ROOT
*
*/
const DEP_ROOT = 'packages';
/**
* Cache directory for
*
* @var string CACHE_DIR
*
*/
const CACHE_DIR = 'cache';
/**
*
* Cache all the bundled assets
*
* @var bool ASSET_CACHE
*
*/
const ASSET_CACHE = true;
<?php
/**
* Model for the CouchDB-React-Bingo project
* Abstracts the interaction with the fauxton-client package
*
* @package The Bingo Framework
* @author Lochemem Bruno Michael
*
*/
namespace App\Models;
use \App\Config;
final class CouchBasketball
{
protected $db;
protected $doc;
public function __construct($db, $doc)
{
$this->db = $db;
$this->doc = $doc;
}
/**
* Create the database if it does not exist
* Use this only if you do not want to create the database via the Fauxton interface
*
* @return array $db->createDatabase(Config::DB_NAME) Database information
*/
public function createDatabase()
{
if (in_array(Config::DB_NAME, $this->db->showAllDatabases())) {
return false;
}
return $this->db->createDatabase(Config::DB_NAME);
}
/**
* Get all the documents from the specified database
*
* @return string $doc->getDocsByKey($params) CouchDB JSON documents
*/
public function getAllDocs()
{
$rows = $this->doc->showAllDocuments(Config::DB_NAME)->rows;
$keys = [];
for ($x = 0; $x < count($rows); $x++) {
$keys[$x] = $rows[$x]->key;
}
$this->doc::setReturnType(true);
return $this->doc->getDocsByKey(Config::DB_NAME, ['keys' => $keys]);
}
/**
* Create a document
*
* @param array $doc An array of document information
* @return array $doc->createDocument($params) CouchDB document details
*/
public function createDoc(array $docBody)
{
$docId = $this->doc->generateId()->uuids[0];
return $this->doc->createDocument($docId, Config::DB_NAME, $docBody);
}
}
<?php
/**
* Public file for the Bingo Framework
* Adding routes for the various controllers and views happens here
*
* @package The Bingo Framework
* @author Lochemem Bruno Michael
*
*/
require dirname(__DIR__) . '/packages/autoload.php';
set_error_handler('Core\Error::errorHandler');
set_exception_handler('Core\Error::exceptionHandler');
$router = new Core\Router();
$router->inject('Views', function ($c) {
return new \Core\Views;
});
$router->inject('CouchDb', function ($c) {
$db = new \Chemem\Fauxton\DatabaseActions;
$doc = new \Chemem\Fauxton\DocumentActions;
$doc->setReturnType(false);
$db->useFauxtonLogin(\App\Config::DB_USER, \App\Config::DB_PASS);
return new \App\Models\CouchBasketball($db, $doc);
});
$router->addRoute('{controller}/{action}');
$router->dispatch($_SERVER['QUERY_STRING']);
body {
padding: 0;
margin: 0;
font-family: Verdana;
}
/**
* Entry form styles
*/
.entry-form, .entry-list {
width: 50%;
height: auto;
display: flex;
flex-flow: column;
flex-direction: column;
margin-left: 25%;
margin-right: 25%;
border: 1px solid dashed;
}
.entry-form-input, .entry-form-button {
width: 100%;
border: none;
font-size: 18px;
position: relative;
margin-top: 5px;
margin-bottom: 5px;
padding-top: 5px;
padding-bottom: 5px;
}
.entry-form-input:focus {
outline: none;
border-bottom: 2px solid black;
}
.entry-form-button {
cursor: pointer;
font-size: 21px;
background: royalblue;
}
/**
* Entry list styles
*/
.entry-list {
margin: 0;
padding: 0;
flex-wrap: nowrap;
}
.entry-list-node {
margin-top: 1%;
margin-bottom: 1%;
}
"use strict";
(function () {
const reactElement = React.createElement;
function ajaxRequest(method, data) {
data = (typeof data !== 'undefined') ? data : ''; //data to be sent
return new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest;
if (method === 'POST') {
ajax.open('POST', '/basketball/add-data', true); //add data if method is post
} else {
ajax.open('GET', '/basketball/get-data', true); //pull data if method is get
}
ajax.onreadystatechange = () => {
if (ajax.readyState === 4) {
resolve(ajax.response);
}
};
ajax.onerror = () => {
reject(ajax.status);
}
ajax.send(data);
});
}
class EntryForm extends React.Component {
constructor(props) {
super(props);
this.state = {
player_name: '',
player_team: '',
player_scoring_avg: ''
};
this.setPlayerName = this.setPlayerName.bind(this);
this.setPlayerTeam = this.setPlayerTeam.bind(this);
this.setPlayerScoringAvg = this.setPlayerScoringAvg.bind(this);
this.submitData = this.submitData.bind(this);
}
setPlayerName(event) {
this.setState({player_name: event.target.value});
}
setPlayerTeam(event) {
this.setState({player_team: event.target.value});
}
setPlayerScoringAvg(event) {
this.setState({player_scoring_avg: event.target.value});
}
submitData(event) {
event.preventDefault();
ajaxRequest('POST', JSON.stringify(this.state))
.then((response) => {
console.log(response);
}, (error) => {
console.log(error);
});
}
render() {
const renderData = {
playerName: {
placeholder: this.props.placeholder.name,
className: this.props.class.name,
onChange: this.setPlayerName,
value: this.state.playerName,
type: this.props.type.name
},
playerTeam: {
placeholder: this.props.placeholder.team,
className: this.props.class.team,
onChange: this.setPlayerTeam,
value: this.state.playerTeam,
type: this.props.type.team
},
playerScoringAvg: {
placeholder: this.props.placeholder.avg,
className: this.props.class.avg,
onChange: this.setPlayerScoringAvg,
value: this.state.playerScoringAvg,
type: this.props.type.avg
},
submit: {
className: this.props.class.submit,
type: this.props.type.submit,
onClick: this.submitData,
value: 'Submit'
}
};
return reactElement('form', {className: 'entry-form'},
reactElement('input', renderData.playerName),
reactElement('input', renderData.playerTeam),
reactElement('input', renderData.playerScoringAvg),
reactElement('input', renderData.submit)
);
}
}
class DatabaseInfo extends React.Component {
constructor(props) {
super(props);
this.state = {player_data: []};
}
componentDidMount() {
ajaxRequest('GET')
.then((response) => {
this.setState({player_data: JSON.parse(response).rows});
}, (error) => {
console.log(error);
});
}
render() {
return reactElement('ul', {className: this.props.class.ul}, this.state.player_data.map((index) => {
return reactElement('li', {className: this.props.class.li}, index.doc.player_name + ' ' + index.doc.player_scoring_avg + ' PPG')
}));
}
}
ReactDOM.render(
reactElement(DatabaseInfo, {
class: {
ul: 'entry-list',
li: 'entry-list-node'
}
}),
document.getElementById('entry-details')
);
ReactDOM.render(
reactElement(EntryForm, {
placeholder: {
name: 'Player name..',
team: 'Player team..',
avg: 'Player Scoring average'
},
type: {
name: 'text',
team: 'text',
avg: 'number',
submit: 'submit'
},
class: {
avg: 'entry-form-input',
name: 'entry-form-input',
team: 'entry-form-input',
submit: 'entry-form-button'
}
}),
document.getElementById('entry-form')
);
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment