Working version 1

This commit is contained in:
2020-12-18 00:09:51 -03:00
29 changed files with 479 additions and 0 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
BASE_URL=/

7
.gitignore vendored
View File

@ -2,5 +2,12 @@
/vendor/
composer.lock
# NPM
/node_modules/
package-lock.json
# Environment
.env
# Blade
/cache/

17
Readme.md Normal file
View File

@ -0,0 +1,17 @@
# ProVM Basic Web Project
Basic structure for files and folders common for every web project.
## Includes
+ [Slim](http://slimframework.com/)
+ [Nyholm/Psr7](https://github.com/Nyholm/psr7)
+ [Nyholm/Psr7-Server](https://github.com/Nyholm/psr7-server)
+ [PHP-DI/Slim-Bridge](https://github.com/PHP-DI/Slim-Bridge)
+ [Slim-Blade](https://github.com/rubellum/Slim-Blade-View)
+ [Slim-Whoops](https://github.com/zeuxisoo/php-slim-whoops)
+ [PHP dotenv](https://github.com/vlucas/phpdotenv)
+ [jQuery](https://jquery.com/)
+ [Fomantic-UI](https://github.com/fomantic/Fomantic-UI)
+ [PHPUnit](https://phpunit.de/)
+ [Kint](https://kint-php.github.io/kint/)

5
common/Alias/View.php Normal file
View File

@ -0,0 +1,5 @@
<?php
namespace ProVM\Common\Alias;
interface View {
}

View File

@ -0,0 +1,12 @@
<?php
namespace ProVM\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Alias\View;
class Home {
public function __invoke(Request $request, Response $response, View $view) {
return $view->render($response, 'home');
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace ProVM\Common\Definition;
use Slim\Views\Blade;
use ProVM\Common\Alias\View as ViewInterface;
class View extends Blade implements ViewInterface {
}

21
common/Helper/Merger.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace ProVM\Common\Helper;
class Merger {
protected $separator;
public function __construct(string $separator) {
$this->separator = $separator;
}
protected $array;
public function start(): Merger {
$this->array = [];
return $this;
}
public function add($element): Merger {
$this->array []= $element;
return $this;
}
public function merge() {
return implode($this->separator, $this->array);
}
}

58
common/Helper/Tree.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace ProVM\Common\Helper;
class Tree {
protected $merger;
public function __construct($separator) {
$this->merger = new Merger($separator);
}
protected $tree;
public function add($value, $parent = null, $alias = null): Tree {
if ($parent === null) {
if ($alias === null) {
$this->tree []= $value;
return $this;
}
$this->tree[$alias] = $value;
return $this;
}
if (!isset($this->tree[$parent])) {
return $this;
}
if ($alias === null) {
$this->tree []= $this->merger->start()
->add($this->tree[$parent])
->add($value)
->merge();
return $this;
}
$this->tree[$alias] = $this->merger->start()
->add($this->tree[$parent])
->add($value)
->merge();
return $this;
}
public function check_keys($tree) {
$bool = true;
foreach ($tree as $def) {
if (!isset($def[2])) {
continue;
}
$key = $def[2];
$bool &= array_key_exists($key, $this->tree);
}
return $bool;
}
public function addTree($tree) {
while (!$this->check_keys($tree)) {
foreach ($tree as $def) {
$this->add($def[0], $def[1] ?? null, $def[2] ?? null);
}
}
}
public function build($tree) {
$this->tree = [];
$this->addTree($tree);
return (object) $this->tree;
}
}

30
composer.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "provm/base_web",
"description": "Base web project",
"type": "project",
"license": "UNLICENSED",
"authors": [
{
"name": "Aldarien",
"email": "aldarien85@gmail.com"
}
],
"require": {
"slim/slim": "^4.7",
"nyholm/psr7": "^1.3",
"nyholm/psr7-server": "^1.0",
"rubellum/slim-blade-view": "^0.1.1",
"zeuxisoo/slim-whoops": "^0.7.3",
"php-di/slim-bridge": "^3.1",
"vlucas/phpdotenv": "^5.2"
},
"require-dev": {
"phpunit/phpunit": "^8.5",
"kint-php/kint": "^3.3"
},
"autoload": {
"psr-4": {
"ProVM\\Common\\": "common"
}
}
}

66
gulpfile.js Normal file
View File

@ -0,0 +1,66 @@
const {src, dest, series, parallel} = require('gulp')
const fs = require('fs')
const path = require('path')
const concat = require('gulp-concat')
const sourcemaps = require('gulp-sourcemaps')
const babel = require('gulp-babel')
const uglify = require('gulp-uglify')
const rename = require('gulp-rename')
const merge = require('merge-stream')
const sass = require('gulp-dart-sass')
let base_dir_js = 'resources/assets/scripts/'
let output_dir_js = 'public/assets/scripts/'
function getFolders(dir) {
return fs.readdirSync(dir)
.filter(function(file) {
return fs.statSync(path.join(dir, file)).isDirectory()
})
}
function bundle_base_js(done) {
return src(base_dir_js + '*.js')
.pipe(concat('main.js'))
.pipe(dest(output_dir_js))
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(babel())
.pipe(uglify())
.pipe(rename('main.min.js'))
.pipe(sourcemaps.write('./'))
.pipe(dest(output_dir_js))
}
function bundle_js(done) {
let folders = getFolders(base_dir_js)
let tasks = folders.map(function(folder) {
return src(path.join(base_dir_js, folder, '**/*.js'))
.pipe(concat(folder + '.js', {newLine: ';'}))
.pipe(dest(output_dir_js))
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(babel())
.pipe(uglify())
.pipe(rename(folder + '.min.js'))
.pipe(sourcemaps.write('./'))
.pipe(dest(output_dir_js))
})
return merge(tasks)
}
let base_dir_sass = 'resources/assets/sass/'
let output_dir_css = 'public/assets/styles/'
function bundle_base_sass(done) {
return src([base_dir_sass + '*.scss', '!' + base_dir_sass + '_*.scss'])
.pipe(sass())
.pipe(rename({extname: '.css'}))
.pipe(dest(output_dir_css))
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
.pipe(rename({extname: '.min.css'}))
.pipe(sourcemaps.write('./'))
.pipe(dest(output_dir_css))
}
exports.default = series(bundle_base_js, bundle_js, bundle_base_sass)

25
package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "provm-base-web",
"version": "1.0.0",
"description": "Proyecto Base Web de ProVM",
"main": "index.js",
"scripts": {
"test": "mocha"
},
"author": "Aldarien",
"license": "UNLICENSED",
"devDependencies": {
"browserify": "^17.0.0",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-buffer": "^0.0.2",
"gulp-concat": "^2.6.1",
"gulp-dart-sass": "^1.0.2",
"gulp-rename": "^2.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-tap": "^2.0.0",
"gulp-uglify": "^3.0.2",
"merge-stream": "^2.0.0",
"mocha": "^8.0.0"
}
}

4
public/.htaccess Normal file
View File

@ -0,0 +1,4 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

9
public/index.php Normal file
View File

@ -0,0 +1,9 @@
<?php
$__environment = 'web';
include_once implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__),
'setup',
'app.php'
]);
$app->run();

16
resources/routes/web.php Normal file
View File

@ -0,0 +1,16 @@
<?php
use ProVM\Common\Helper\Merger;
use ProVM\Common\Controller\Home;
$folder = (new Merger(DIRECTORY_SEPARATOR))->add(__DIR__)->add($__environment)->merge();
if (file_exists($folder)) {
$files = new DirectoryIterator($folder);
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'php') {
continue;
}
include_once $file->getRealPath();
}
}
$app->get('/', Home::class);

View File

@ -0,0 +1,7 @@
@extends('layout.base')
@section('page_content')
<div class="ui center aligned container">
<h1 class="header">Home</h1>
</div>
@endsection

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<html lang="es">
@include('layout.head')
@include('layout.body')
</html>

View File

@ -0,0 +1,5 @@
<body>
@include('layout.header')
@yield('page_content')
@include('layout.footer')
</body>

View File

@ -0,0 +1,3 @@
<footer>
</footer>
@include('layout.scripts')

View File

@ -0,0 +1,5 @@
<head>
<meta charset="utf-8" />
<title>Control de Gestión</title>
@include('layout.styles')
</head>

View File

@ -0,0 +1,2 @@
<header>
</header>

View File

@ -0,0 +1,7 @@
@if (isset($assets) and isset($assets['scripts']))
@foreach ($assets['scripts'] as $script)
{!!$script!!}
@endforeach
@endif
@stack('scripts')

View File

@ -0,0 +1,15 @@
@if (isset($assets))
@foreach ($assets as $type => $list)
@foreach ($list as $style)
@if ($type == 'styles')
<link rel="stylesheet" href="{{$style}}" />
@elseif ($type == 'fonts')
<link href={{$style}} />
@elseif ($type == 'links')
{!!$style!!}
@endif
@endforeach
@endforeach
@endif
@stack('styles')

52
setup/app.php Normal file
View File

@ -0,0 +1,52 @@
<?php
use DI\ContainerBuilder as Builder;
use DI\Bridge\Slim\Bridge;
include_once 'composer.php';
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->load();
$builder = new Builder();
$folders = [
'common',
$__environment
];
$files = [
'config',
'setup'
];
foreach ($files as $file) {
foreach ($folders as $folder) {
$filename = implode(DIRECTORY_SEPARATOR, [
__DIR__,
$folder,
$file . '.php'
]);
if (!file_exists($filename)) {
continue;
}
$builder->addDefinitions($filename);
}
}
$container = $builder->build();
$app = Bridge::create($container);
$app->setBasePath($container->get('base_url'));
foreach ($folders as $folder) {
$filename = implode(DIRECTORY_SEPARATOR, [
__DIR__,
$folder,
'middleware.php'
]);
if (!file_exists($filename)) {
continue;
}
include_once $filename;
}
$app->addRoutingMiddleware();
include_once 'router.php';

16
setup/common/config.php Normal file
View File

@ -0,0 +1,16 @@
<?php
use ProVM\Common\Helper\Tree;
return [
'debug' => true,
'base_url' => $_ENV['BASE_URL'],
'folders' => function() {
$directory = new Tree(DIRECTORY_SEPARATOR);
return $directory->build([
[dirname(__DIR__, 2), null, 'base'],
['public', 'base', 'public'],
['resources', 'base', 'resources'],
['routes', 'resources', 'routes']
]);
}
];

6
setup/composer.php Normal file
View File

@ -0,0 +1,6 @@
<?php
include_once implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__),
'vendor',
'autoload.php'
]);

5
setup/router.php Normal file
View File

@ -0,0 +1,5 @@
<?php
include_once implode(DIRECTORY_SEPARATOR, [
$app->getContainer()->get('folders')->routes,
$__environment . '.php'
]);

50
setup/web/config.php Normal file
View File

@ -0,0 +1,50 @@
<?php
use Psr\Container\ContainerInterface as Container;
use ProVM\Common\Helper\Tree;
use ProVM\Common\Helper\Merger;
return [
'folders' => DI\decorate(function($prev, Container $c) {
$merger = new Merger(DIRECTORY_SEPARATOR);
$arr = (array) $prev;
$arr['templates'] = $merger->start()->add($prev->resources)->add('views')->merge();
$arr['cache'] = $merger->start()->add($prev->base)->add('cache')->merge();
return (object) $arr;
}),
'urls' => function(Container $c) {
$tree = new Tree('/');
return $tree->build([
[$c->get('base_url'), null, 'base'],
['assets', 'base', 'assets'],
['images', 'assets', 'images'],
['scripts', 'assets', 'scripts'],
['styles', 'assets', 'styles']
]);
},
'assets' => function(Container $c) {
$arr = [
'links' => [
'<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.css" integrity="sha512-g/MzOGVPy3OQ4ej1U+qe4D/xhLwUn5l5xL0Fa7gdC258ZWVJQGwsbIR47SWMpRxSPjD0tfu/xkilTy+Lhrl3xg==" crossorigin="anonymous" />'
],
'fonts' => [
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/icons.eot',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/icons.ttf',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/icons.woff',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/icons.woff2',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/outline-icons.eot',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/outline-icons.ttf',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/outline-icons.woff',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/outline-icons.woff2',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/brand-icons.eot',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/brand-icons.ttf',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/brand-icons.woff',
'https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/themes/default/assets/fonts/brand-icons.woff2'
],
'scripts' => [
'<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>',
'<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.js" integrity="sha512-1Nyd5H4Aad+OyvVfUOkO/jWPCrEvYIsQENdnVXt1+Jjc4NoJw28nyRdrpOCyFH4uvR3JmH/5WmfX1MJk2ZlhgQ==" crossorigin="anonymous"></script>'
]
];
return $arr;
}
];

2
setup/web/middleware.php Normal file
View File

@ -0,0 +1,2 @@
<?php
$app->add(new Zeuxisoo\Whoops\Slim\WhoopsMiddleware());

20
setup/web/setup.php Normal file
View File

@ -0,0 +1,20 @@
<?php
use Psr\Container\ContainerInterface as Container;
return [
ProVM\Common\Alias\View::class => function(Container $c) {
if (!file_exists($c->get('folders')->cache)) {
mkdir($c->get('folders')->cache);
chmod($c->get('folders')->cache, 777);
}
return new ProVM\Common\Definition\View(
$c->get('folders')->templates,
$c->get('folders')->cache,
null,
[
'urls' => $c->get('urls'),
'assets' => $c->get('assets')
]
);
}
];