1
0
Fork 0

Webseite aus Archiv

This commit is contained in:
Ruben Meyer 2020-07-04 12:07:02 +02:00
parent 3258ce6a56
commit 934fb39856
Signed by: rxbn_
GPG Key ID: BE3BF898BE352FE2
213 changed files with 17900 additions and 1 deletions

View File

@ -1,5 +1,5 @@
# SVEN - Schulprojekt 2019
SVEN - Lager Verwaltung mit Simulation und etwaigen Features
SVEN - Supermarkt Verwaltung mit Simulation und etwaigen Features
## Features
- Kasse

59
webseite/README.md Normal file
View File

@ -0,0 +1,59 @@
# SVEN
SVEN is a supermarket management system.
## Requirements
- PHP Version 5.5 or higher
- because of password_* functions
## File Structure
- pages/ - Pages
- public/ - CSS & Javascript Files
- sys/ - Core and Framework Files
- mysql/ - MySQL Connection Wrapper
- security/ - Authentication & miscellaneous
- sources/ - Used Libraries
- sven/ - Some important classes
- templates/ - prebuilt elements
## API Request Methods
- GET - Read from database
- POST - Insert to database
- PATCH - Update in database
- DELETE - Delete from database
## API Output Scheme
```json
{
"state": "failed", // program state
"access": "granted|denied", // authentication
"msg": "{{custom_data_msg}}",
"data": "{{data}}",
"error": ["{{error}}", ...] // optional
}
```
## API Parameters
- article
- GET
- limit - limit output
- search_for - search for value
- in - column name to search in
## API Errors
- REQUEST_METHOD_NOT_SUPPORTED -> Request method not supported
- AUTHENTICATION_FAILED -> User not logged in. / Username or password wrong.
- BAD_REQUEST -> request or information are missing
- NO_OUTPUT -> No data was passed. Request unfortunately failed.
## Credits
- Ruben Meyer
- Bennet Kahrs
- [envms (fluentpdo)][fluentpdo]
- [Luís Cobucci (jwt library)][lcobucci]
## Not working things
- can't install laravel / composer to use an popular framework, so we had to write our own framework
[fluentpdo]: <http://envms.github.io/fluentpdo/>
[lcobucci]: <https://github.com/lcobucci/jwt>

56
webseite/api/article.php Normal file
View File

@ -0,0 +1,56 @@
<?php
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if($auth->loggedIn()) {
switch($_SERVER["REQUEST_METHOD"]) {
case 'GET':
$array = (object) [];
$query = $fluent->from('t_artikel')->select('t_artikel.*');
if(isset($_GET['limit']) && !is_nan($_GET['limit'])) $query->limit($_GET['limit']);
if(isset($_GET['search_for']) && isset($_GET['in'])) $query->where($_GET['in'].' LIKE ?', $_GET['search_for'].'%');
$rows = $query->fetchAll();
$data = [];
foreach ($rows as $arr) {
$data[] = $arr;
}
$array->data = ["DATA" => $data];
$array->state = "successed";
break;
case 'POST':
$array = (object) [];
$array->data = ["POST" => \sven\sys\sven\web::getRequestBody()];
$array->state = "successed";
break;
case 'PATCH':
$array = (object) ["DATA" => ["PATCH" => \sven\sys\sven\web::getRequestBody()]];
break;
case 'DELETE':
$array = (object) [];
break;
}
if(empty($array)) {
$array = (object) [
"msg" => "No output data was passed. Request unfortunately failed.",
"error" => ["NO_OUTPUT"]
];
}
} else {
$array = (object) [
"body" => $body,
"msg" => "User not logged in.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

60
webseite/api/auth.php Normal file
View File

@ -0,0 +1,60 @@
<?php
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$session = new \sven\sys\security\session;
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if(!$auth->loggedIn()) {
$user = (isset($body->user)) ? $body->user : (isset($_GET['user']) ? $_GET['user'] : null);
$pass = (isset($body->pass)) ? $body->pass : (isset($_GET['pass']) ? $_GET['pass'] : null);
$csrf = (isset($body->csrf)) ? $body->csrf : (isset($_GET['csrf']) ? $_GET['csrf'] : null);
if($user && $pass && $csrf) {
$query = $fluent->from('t_benutzer')->select('t_benutzer.*')->where("Name = ?", $user);
$data = $query->fetch();
// verify user exists and password is right
if($data && $auth->verifyPassword($pass, $data->Passwort)) {
if($auth->validateCSRF("login", $csrf)) {
$array = (object) [
"token" => $auth->login($user),
"msg" => "User logged in.",
"state" => "successed"
];
} else {
// CSRF wrong
$array = (object) [
"msg" => "CSRF Code wrong.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
} else {
// Username not found or password wrong
$array = (object) [
"msg" => "Username or password wrong.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
} else {
// BAD_REQUEST
$array = (object) [
"session" => $session->read("csrf_form_login"),
"body" => $body,
"msg" => "Could process request. Missing data?",
"error" => ["BAD_REQUEST"]
];
}
} else {
$auth->logout();
// USER LOGGED IN; LOGOUT
$array = (object) [
"msg" => "User logged out.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

View File

@ -0,0 +1,8 @@
<?php
$array = (object) [
"msg" => "API Interface not found. Spelling wrong?"
];
\sven\sys\core::replaceApiOutput($array);
?>

View File

@ -0,0 +1,57 @@
<?php
// @TODO innerJoin to article
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if($auth->loggedIn()) {
switch($_SERVER["REQUEST_METHOD"]) {
case 'GET':
$array = (object) [];
$query = $fluent->from('t_verkaufszahlen')->select('t_verkaufszahlen.*');
if(isset($_GET['limit']) && !is_nan($_GET['limit'])) $query->limit($_GET['limit']);
if(isset($_GET['search_for']) && isset($_GET['in'])) $query->where($_GET['in'].' LIKE ?', $_GET['search_for'].'%');
$rows = $query->fetchAll();
$data = [];
foreach ($rows as $arr) {
$data[] = $arr;
}
$array->data = ["DATA" => $data];
$array->state = "successed";
break;
case 'POST':
$array = (object) [];
$array->data = ["POST" => \sven\sys\sven\web::getRequestBody()];
$array->state = "successed";
break;
case 'PATCH':
$array = (object) ["DATA" => ["PATCH" => \sven\sys\sven\web::getRequestBody()]];
break;
case 'DELETE':
$array = (object) [];
break;
}
if(empty($array)) {
$array = (object) [
"msg" => "No output data was passed. Request unfortunately failed.",
"error" => ["NO_OUTPUT"]
];
}
} else {
$array = (object) [
"body" => $body,
"msg" => "User not logged in.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

View File

@ -0,0 +1,55 @@
<?php
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if($auth->loggedIn()) {
switch($_SERVER["REQUEST_METHOD"]) {
case 'GET':
$array = (object) [];
$query = $fluent->from('t_statistik')->select('t_statistik.*');
if(isset($_GET['limit']) && !is_nan($_GET['limit'])) $query->limit($_GET['limit']);
if(isset($_GET['search_for']) && isset($_GET['in'])) $query->where($_GET['in'].' LIKE ?', $_GET['search_for'].'%');
$rows = $query->fetchAll();
$data = [];
foreach ($rows as $arr) {
$data[] = $arr;
}
$array->data = ["DATA" => $data];
$array->state = "successed";
break;
case 'POST':
$array = (object) [];
$array->data = ["POST" => \sven\sys\sven\web::getRequestBody()];
$array->state = "successed";
break;
case 'PATCH':
$array = (object) ["DATA" => ["PATCH" => \sven\sys\sven\web::getRequestBody()]];
break;
case 'DELETE':
$array = (object) [];
break;
}
if(empty($array)) {
$array = (object) [
"msg" => "No output data was passed. Request unfortunately failed.",
"error" => ["NO_OUTPUT"]
];
}
} else {
$array = (object) [
"msg" => "User not logged in.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

65
webseite/api/stock.php Normal file
View File

@ -0,0 +1,65 @@
<?php
// @TODO innerJoin to article
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if($auth->loggedIn()) {
switch($_SERVER["REQUEST_METHOD"]) {
case 'GET':
$array = (object) [];
$query = $fluent->from('t_lager')->select('t_lager.*');
$query_2 = $fluent->from('t_artikel')->select('t_artikel.*');
if(isset($_GET['limit']) && !is_nan($_GET['limit'])) $query->limit($_GET['limit']);
if(isset($_GET['search_for']) && isset($_GET['in'])) $query->where($_GET['in'].' LIKE ?', $_GET['search_for'].'%');
$rows = $query->fetchAll();
$rows_2 = $query_2->fetchAll();
$names = [];
foreach ($rows_2 as $key => $row) {
$names[$row->ID] = $row->Name;
}
$data = [];
foreach ($rows as $key => $row) {
$row->Name = $names[$row->f_Artikel_ID];
$data[] = $row;
}
$array->data = ["DATA" => $data];
$array->state = "successed";
break;
case 'POST':
$array = (object) [];
$array->data = ["POST" => \sven\sys\sven\web::getRequestBody()];
$array->state = "successed";
break;
case 'PATCH':
$array = (object) ["DATA" => ["PATCH" => \sven\sys\sven\web::getRequestBody()]];
break;
case 'DELETE':
$array = (object) [];
break;
}
if(empty($array)) {
$array = (object) [
"msg" => "No output data was passed. Request unfortunately failed.",
"error" => ["NO_OUTPUT"]
];
}
} else {
$array = (object) [
"body" => $body,
"msg" => "User not logged in.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

View File

@ -0,0 +1,66 @@
<?php
// @TODO innerJoin to article
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if($auth->loggedIn()) {
switch($_SERVER["REQUEST_METHOD"]) {
case 'GET':
$array = (object) [];
$query = $fluent->from('t_bestellungen')->select('t_bestellungen.*');
$query_2 = $fluent->from('t_artikel')->select('t_artikel.*');
if(isset($_GET['limit']) && !is_nan($_GET['limit'])) $query->limit($_GET['limit']);
if(isset($_GET['search_for']) && isset($_GET['in'])) $query->where($_GET['in'].' LIKE ?', $_GET['search_for'].'%');
$rows = $query->fetchAll();
$rows_2 = $query_2->fetchAll();
$names = [];
foreach ($rows_2 as $key => $row) {
$names[$row->ID] = $row->Name;
}
$data = [];
foreach ($rows as $key => $row) {
$row->Name = $names[$row->f_Artikel_ID];
$data[] = $row;
}
$array->data = ["DATA" => $data];
$array->state = "successed";
break;
case 'POST':
$array = (object) [];
$array->data = ["POST" => \sven\sys\sven\web::getRequestBody()];
$array->state = "successed";
break;
case 'PATCH':
$array = (object) ["DATA" => ["PATCH" => \sven\sys\sven\web::getRequestBody()]];
break;
case 'DELETE':
$array = (object) [];
break;
}
if(empty($array)) {
$array = (object) [
"msg" => "No output data was passed. Request unfortunately failed.",
"error" => ["NO_OUTPUT"]
];
}
} else {
$array = (object) [
"body" => $body,
"msg" => "User not logged in.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

61
webseite/api/users.php Normal file
View File

@ -0,0 +1,61 @@
<?php
// @TODO Comments
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") { \sven\sys\core::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));}
$body = \sven\sys\sven\web::getRequestBody();
$array = (object) [];
if($auth->loggedIn()) {
switch($_SERVER["REQUEST_METHOD"]) {
case 'GET':
$array = (object) [];
$query = $fluent->from('t_benutzer')->select('t_benutzer.*');
if(isset($_GET['limit']) && !is_nan($_GET['limit'])) $query->limit($_GET['limit']);
if(isset($_GET['search_for']) && isset($_GET['in'])) $query->where($_GET['in'].' LIKE ?', $_GET['search_for'].'%');
$rows = $query->fetchAll();
$data = [];
foreach ($rows as $row) {
$row->Passwort = null;
$data[] = $row;
}
$array->data = ["DATA" => $data];
$array->state = "successed";
break;
case 'POST':
$array = (object) [];
if(!empty($body = \sven\sys\sven\web::getRequestBody())) {
if(isset($body->user) && isset($body->pass)) {
$query = $fluent->from('t_benutzer')->select('t_benutzer.*');
$array->data = ["POST" => $body];
$array->state = "successed";
}
}
break;
case 'PATCH':
$array = (object) ["DATA" => ["PATCH" => \sven\sys\sven\web::getRequestBody()]];
break;
case 'DELETE':
$array = (object) [];
break;
}
if(empty($array)) {
$array = (object) [
"msg" => "No output data was passed. Request unfortunately failed.",
"error" => ["NO_OUTPUT"]
];
}
} else {
$array = (object) [
"body" => $body,
"msg" => "User not logged in.",
"error" => ["AUTHENTICATION_FAILED"]
];
}
\sven\sys\core::replaceApiOutput($array);
?>

30
webseite/index.php Normal file
View File

@ -0,0 +1,30 @@
<?php
// version check
if(version_compare(phpversion(), '7.0.0', '<')) exit('PHP VERSION insufficient ('.phpversion().')');
// class autoload
require('sys/autoload.php');
use \sven\sys\sven as sven;
use \sven\sys\mysql\mysql as mysql;
use \sven\sys\core as CORE;
// Request Handler
sven\web::setSecurityHeaders();
$web = new sven\web(dirname(__FILE__));
/* TOOL: prebuilt source files for used libraries
$folder = dirname(__FILE__).'/sys/sources/';
foreach(sven\utilities::getAllFiles($folder, '.php') as $key) {
$key = str_replace($folder.'\jwt\\src\\', '$dir.\'', $key);
if(sven\utilities::startsWith($key, '$dir')) print_r($key."', ");
}
/**/
//print_r(get_required_files());
if(!sven\web::$API)
print_r(CORE::getOutput());
else
echo(CORE::getApiOutput());
?>

112
webseite/pages/abcxyz.php Normal file
View File

@ -0,0 +1,112 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'ABC/XYZ Analyse');
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
function lastElement($array) {return end($array);}
$options = "";
if($auth->loggedIn()) {
$query_2 = $fluent->from('t_artikel')->select('t_artikel.Name, t_artikel.ID');
$rows_2 = $query_2->fetchAll();
$names = [];
foreach ($rows_2 as $id => $row) {
$names[$row->ID] = $row->Name;
}
$query = $fluent->from('t_verkaufszahlen')->select('t_verkaufszahlen.*');
$rows = $query->fetchAll();
$data = [
'abc' => [
'mengen' => [],
'werte' => []
],
'xyz' => [],
'sorted_abc' => [0 => [], 1 => [], 2 => []],
'sorted_xyz' => [0 => [], 1 => [], 2 => []]
];
$output = "";
foreach ($rows as $id => $row) {
if($id > 0) $data['abc']['mengen'][$id] = [ ($row->Menge+$data['abc']['mengen'][$id-1][0]), $row->f_Artikel_ID];
else $data['abc']['mengen'][$id] = [$row->Menge+0, $row->f_Artikel_ID];
if($id > 0) $data['abc']['werte'][$id] = [($row->Verkaufspreis+$data['abc']['werte'][$id-1][0]), $row->f_Artikel_ID];
else $data['abc']['werte'][$id] = [$row->Verkaufspreis+0, $row->f_Artikel_ID];
$output .= "<tr>";
$output .="<td>{$names[$data['abc']['mengen'][$id][1]]}</td>";
$output .="<td>{$data['abc']['mengen'][$id][0]}</td>";
$output .="<td>{$data['abc']['werte'][$id][0]}</td>";
$output .="</tr>";
}
// ABC Zuordnung
foreach ($data['abc']['mengen'] as $key => $value) {
$value = $data['abc']['werte'][$key][0] / lastElement($data['abc']['werte'])[0];
//var_dump([$value > 0.8, 0.8 < $value && $value > 0.95, 0.95 < $value]);
if($value > 0.95) array_push($data['sorted_abc'][2], $names[$data['abc']['werte'][$key][1]]);
else if(0.8 > $value && $value < 0.95) array_push($data['sorted_abc'][1], $names[$data['abc']['werte'][$key][1]]);
else if($value < 0.8) array_push($data['sorted_abc'][0], $names[$data['abc']['werte'][$key][1]]);
//var_dump($value);
}
$XA = var_export($data['sorted_abc'][0], true);
$XB = var_export($data['sorted_abc'][1], true);
$XC = var_export($data['sorted_abc'][2], true);
$YA = "";
$YB = "";
$YC = "";
$ZA = "";
$ZB = "";
$ZC = "";
}
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container" id="calc-page">
<div class="row">
<div class="col-md-12">
<h2>ABC/XYZ Analyse</h2>
<table>
<thead>
<tr>
<td>Verbrauchsmenge</td>
<td>A</td>
<td>B</td>
<td>C</td>
</tr>
</thead>
<tbody>
<tr>
<td>X</td>
<td>{$XA}</td>
<td>{$XB}</td>
<td>{$XC}</td>
</tr>
<tr>
<td>Y</td>
<td>{$YA}</td>
<td>{$YB}</td>
<td>{$YC}</td>
</tr>
<tr>
<td>Z</td>
<td>{$ZA}</td>
<td>{$ZB}</td>
<td>{$ZC}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
EOF
);
?>

149
webseite/pages/calc.php Normal file
View File

@ -0,0 +1,149 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'Kalkulation');
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
$options = "";
if($auth->loggedIn()) {
$query = $fluent->from('t_artikel')->select('t_artikel.*');
$rows = $query->fetchAll();
foreach ($rows as $id => $row) {
$selected = (($row->ID === "1") ? "selected" : "");
$options .= "<option value='{$row->ID}|{$row->Einkaufspreis}|{$row->Anzahl_pro_Palette}' $selected>{$row->Name}</option>";
}
}
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container" id="calc-page">
<div class="row">
<div class="col-md-12">
<h2>Gewinnkalkulation</h2>
<div class="input-group mb-md-3" id="products-selector">
<label for="products">Produkt</label>
<select class="custom-select" id="products">
<!-- <option value="%PRICE%|%PLT_COUNT%"></option> -->
$options
</select>
</div>
<div class="my-md-4 form-row">
<div class="form-group col-md-6">
<label for="selected-price">Preis (GE)</label>
<input type="text" class="form-control" id="selected-price" placeholder="Preis" disabled>
</div>
<div class="form-group col-md-6">
<label for="selected-plt">Anzahl pro Palette (ME)</label>
<input type="text" class="form-control" id="selected-plt" placeholder="Anzahl pro Palette" disabled>
</div>
</div>
<div class="form-row">
<div class="col-md-1"></div>
<div class="form-group col-md-2">
<label for="rabatt">Rabatt (%)</label>
<input type="text" class="form-control" id="rabatt" placeholder="Rabatt">
</div>
<div class="form-group col-md-2">
<label for="skonto">Skonto (%)</label>
<input type="text" class="form-control" id="skonto" placeholder="Skonto">
</div>
<div class="form-group col-md-2">
<label for="bezugskosten">Bezugskosten (%)</label>
<input type="text" class="form-control" id="bezugskosten" placeholder="Bezugskosten">
</div>
<div class="form-group col-md-2">
<label for="handelskosten">Handelskosten (%)</label>
<input type="text" class="form-control" id="handelskosten" placeholder="Handelskosten">
</div>
<div class="form-group col-md-2">
<label for="gewinnzuschlag">Gewinnzuschlag (%)</label>
<input type="text" class="form-control" id="gewinnzuschlag" placeholder="Gewinnzuschlag">
</div>
</div>
<hr />
<!-- CALCULATION FIELDS -->
<h3> Rechnung </h3>
<div class="form-row">
<div class="form-group col-md-6">
<label for="listeneinkaufspreis">Listeneinkaufspreis (GE)</label>
<input type="text" class="form-control" id="listeneinkaufspreis" placeholder="Listeneinkaufspreis" disabled>
</div>
<div class="form-group col-md-6">
<label for="selbstkosten">Selbstkosten (GE) | <span class="info"> &darr; Handelskosten </span></label>
<input type="text" class="form-control selbstkosten" placeholder="Selbstkosten" disabled>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="zieleinkaufspreis">Zieleinkaufspreis (GE) | <span class="info">&darr; Rabatt </span></label>
<input type="text" class="form-control" id="zieleinkaufspreis" placeholder="Zieleinkaufspreis" disabled>
</div>
<div class="form-group col-md-6">
<label for="angebotspreis">Angebotspreis (GE) | <span class="info">&darr; Gewinnzuschlag </span></label>
<input type="text" class="form-control" id="angebotspreis" placeholder="Angebotspreis" disabled>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="bareinkaufspreis">Bareinkaufspreis (GE) | <span class="info">&darr; Skonto </span></label>
<input type="text" class="form-control" id="bareinkaufspreis" placeholder="Bareinkaufspreis" disabled>
</div>
<div class="form-group col-md-6">
<label for="angebotspreis_mwst">Angebotspreis(MwSt.) (19%) (GE) | <span class="info">&darr; Mehrwertsteuer </span></label>
<input type="text" class="form-control" id="angebotspreis_mwst" placeholder="Angebotspreis (MwSt.)" disabled>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="bezugspreis">Bezugspreis (GE) | <span class="info">&darr; Bezugskosten </span></label>
<input type="text" class="form-control" id="bezugspreis" placeholder="Bezugspreis" disabled>
</div>
<div class="form-group col-md-6">
<label for="angebotspreis_stk">Angebotspreis pro Stück (GE)</label>
<input type="text" class="form-control" id="angebotspreis_stk" placeholder="Angebotspreis (Stückzahl)" disabled>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="selbstkosten">Selbstkosten (GE)</label>
<input type="text" class="form-control selbstkosten" placeholder="Selbstkosten" disabled>
</div>
<div class="form-group col-md-6">
<label for="rohgewinn">Rohgewinn (GE)</label>
<input type="text" class="form-control bg-success" id="rohgewinn" placeholder="Rohgewinn" disabled>
</div>
</div>
<hr />
<!-- Konkurrenzvergleich -->
<h3> Konkurrenzpreis </h3>
<div class="form-row">
<div class="form-group col-md-6">
<label for="selbstkosten">Selbstkosten (GE)</label>
<input type="text" class="form-control selbstkosten" placeholder="Selbstkosten" disabled>
</div>
<div class="form-group col-md-6">
<label for="konkurrenzpreis">Konkurrenzpreis (GE)</label>
<input type="text" class="form-control" id="konkurrenzpreis" placeholder="Konkurrenzpreis">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="gewinn">Gewinn (GE)</label>
<input type="text" class="form-control" id="gewinn" placeholder="Gewinn" disabled>
</div>
<div class="form-group col-md-6">
<label for="gewinnPrcnt">Gewinn (%)</label>
<input type="text" class="form-control" id="gewinnPrcnt" placeholder="Gewinn in Prozent" disabled>
</div>
</div>
</div>
</div>
</div>
EOF
);
?>

View File

@ -0,0 +1,18 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'Lieferungen');
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>Aktuelle Lieferungen</h2>
<p>Wenn es aktuelle Lieferungen gibt, werden diese hier in Tabellenform angezeigt.</p>
<br />
<div id='lieferTabelle'></div>
</div>
</div>
</div>
EOF
);
?>

View File

@ -0,0 +1,19 @@
<?php
use \sven\sys\core as CORE;
CORE::addReplacement('head', 'TITLE', 'Fehler - 404');
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container-fluid">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<h2 class="mb-md-4"> Suchen Sie etwas bestimmtes? </h2>
<p> Die gesuchte Adresse ist nicht verfügbar. Vielleicht ein andermal? </p>
<hr />
<a class="btn btn-info" href="./">Zurück zur Startseite</a>
</div>
<div class="col-md-2"></div>
</div>
</div>
EOF
);
?>

25
webseite/pages/index.php Normal file
View File

@ -0,0 +1,25 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'Dashboard');
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="divH">
<h1 class='hWelcome'>Willkommen bei Sven<h1>
<p class="pH_Wel">Dies ist ihre Supermarkt-Verwaltungs-Software.</p>
<br />
</div>
<br />
<br />
<h2>Produkttabelle</h2>
<div id="produktTabelle"></div>
</div>
</div>
</div>
<br />
<br />
EOF
);
?>

39
webseite/pages/login.php Normal file
View File

@ -0,0 +1,39 @@
<?php
use \sven\sys\core as CORE;
$token = $auth->createCSRF("login");
CORE::addReplacement('head', 'TITLE', 'Login');
CORE::addTemplate('main', <<<EOF
<div class="container-fluid">
<div class="row">
<div class="col-md-12 mx-auto mt-3">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<h2>Login</h2>
<form name="login">
<div class="form-group">
<label for="loginUser">Benutzername</label>
<input type="test" class="form-control" id="loginUser" name="loginUser" placeholder="Gib deinen Benutzernamen ein">
</div>
<div class="form-group">
<label for="loginPass">Passwort</label>
<input type="password" class="form-control" id="loginPass" name="loginPass" placeholder="Und hier dein Passwort">
</div>
<input type="hidden" id="csrf_token" name="csrf_token" value="%%main_CSRF%%">
<div class="row float-right">
<button type="submit" class="btn btn-primary">Login</button>
</div>
</form>
</div>
<div class="col-md-2"></div>
</div>
</div>
</div>
</div>
EOF
);
CORE::addReplacement('main', 'CSRF', $token);
?>

View File

@ -0,0 +1,7 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
if($auth->loggedIn())
$auth->logout();
?>

29
webseite/pages/stats.php Normal file
View File

@ -0,0 +1,29 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'Statistik');
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>Verkaufte Produkte</h2>
<br />
<div class="ct-chart ct-golden-section" id="chart1" onready="gesamtMenge()"></div>
<br />
<br />
<h2>Produkte</h2>
<div id ="products-selector" class="input-group">
<label for="produkte">Produkt auswählen:</label>
<select id="produkte" class="custom-select"></select>
<button class="btn btn-primary" onclick="selectProduct()">Verkaufte Menge</button>
<button class="btn btn-primary" onclick="gewinnGraph()">Gewinngraph</button>
</div>
<br />
<br />
<div class="ct-chart ct-golden-section" id="chart2"></div>
</div>
</div>
</div>
EOF
);
?>

16
webseite/pages/stock.php Normal file
View File

@ -0,0 +1,16 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'Lager');
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>Lagertabelle</h2>
<div id='lagerTabelle'></div>
</div>
</div>
</div>
EOF
);
?>

148
webseite/pages/users.php Normal file
View File

@ -0,0 +1,148 @@
<?php
use \sven\sys\core as CORE;
CORE::setLogin(true);
CORE::addReplacement('head', 'TITLE', 'Benutzerverwaltung');
$mysql = new \sven\sys\mysql\mysql();
$fluent = $mysql->getBuilder();
$tableString = "";
if($auth->loggedIn()) {
$query = $fluent->from('t_benutzer')->select('t_benutzer.*');
$rows = $query->fetchAll();
foreach ($rows as $row) {
$banned = ($row->banned === 1) ? "Ja" : "Nein";
$tableString .= "<tr class='table-py-2'>";
$tableString .= "<td>{$row->ID}</td>";
$tableString .= "<td>{$row->Name}</td>";
$tableString .= "<td>{$banned}</td>";
$tableString .= "<td>";
$tableString .= '<button type="button" class="btn btn-warning" data-toggle="modal" data-user="'.$row->Name.'" data-target="#modalChangePassword">Passwort ändern</button>';
$tableString .= '<span class="mx-2"></span>';
$tableString .= '<button type="button" class="btn btn-danger" data-toggle="modal" data-user="'.$row->Name.'" data-target="#modalDeleteUser">Benutzer löschen</button>';
$tableString .= "</td>";
$tableString .= "</tr>";
}
}
CORE::addReplacement('main', 'FILE', <<<EOF
<div class="container" id="users_page">
<div class="row">
<div class="col-md-12">
<h2>Benutzerverwaltung</h2>
<table class="mb-md-2">
<thead>
<tr>
<th> ID </th>
<th> Benutzername </th>
<th> Gebannt </th>
<th> Aktionen </th>
</tr>
</thead>
<tbody>
$tableString
</tbody>
</table>
<div class="float-right">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#modalAddUser">Benutzer hinzufügen</button>
</div>
<div class="modal fade" id="modalChangePassword" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Passwort ändern</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>
Möchtest du wirklich das Passwort ändern für
"<span class="m-cp-span-user"></span>"?
</p>
<form name="m_cp_form" id="m_cp_form">
<div class="form-group">
<label for="change_username" class="col-form-label">Benutzername</label>
<input type="text" class="form-control" id="change_username" name="username" disabled>
</div>
<div class="form-group">
<label for="password" class="col-form-label">Neues Passwort</label>
<input type="text" class="form-control" id="password" name="password">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary float-right">Abschicken</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalDeleteUser" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Benutzer löschen</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>
Möchtest du wirklich den Benutzer "<span id="m-du-span-user"></span>" löschen?
</p>
<form name="m_du_form" id="m_du_form">
<div class="form-group">
<label for="del_username" class="col-form-label">Benutzername</label>
<input type="text" class="form-control" id="del_username" name="username" disabled>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary float-right">Abschicken</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalAddUser" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Benutzer hinzufügen</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<form name="m_au_form" id="m_au_form">
<div class="form-group">
<label for="add_username" class="col-form-label">Benutzername</label>
<input type="text" class="form-control" id="add_username" name="username">
</div>
<div class="form-group">
<label for="add_password" class="col-form-label">Passwort</label>
<input type="password" class="form-control" id="add_password" name="password">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary float-right">Abschicken</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
EOF
);
?>

6
webseite/public/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,170 @@
body {
font-size: .875rem;
font-family: 'Roboto', sans-serif;
}
/*
* Sidebar
*/
.sidebar {
position: fixed;
padding: 0;
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
.sidebar-sticky {
position: -webkit-sticky;
position: sticky;
top: 48px; /* Height of navbar */
height: calc(100vh - 48px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
transition: all .2s cubic-bezier(1,.1,.13,1);
}
.sidebar .nav-link.active {
color: #007bff;
background-color: #e3e3e3;
}
.sidebar .nav-link:hover:not(.active) {
background-color: #d2d2d2;
color: #0056b3;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
/*
* Navbar
*/
.navbar-brand {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: 1rem;
background-color: rgba(0, 0, 0, .25);
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
}
.navbar .form-control {
padding: .75rem 1rem;
border-width: 0;
border-radius: 0;
}
.form-control-dark {
color: #fff;
background-color: rgba(255, 255, 255, .1);
border-color: rgba(255, 255, 255, .1);
}
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}
/*
* Utilities
*/
.border-top { border-top: 1px solid #e5e5e5; }
.border-bottom { border-bottom: 1px solid #e5e5e5; }
/**
* Charts
* @author Bennet Kahrs
*/
.ct-series-a .ct-bar {
stroke: rgb(0, 154, 174);
stroke-width:20px
}
.ct-series-a .ct-line {
stroke: rgb(0, 154, 174);
}
.ct-series-a .ct-area {
fill: rgba(0, 154, 174,.9);
}
.ct-series-a .ct-point {
stroke: rgb(0, 154, 174);
}
table {
width: 100%;
border: black 1px solid;
}
tbody tr:nth-child(even) {
background-color: rgba(52,58,62, .4);
}
thead {
text-align: center;
color: white;
background-color: rgba(52,58,62, .9);
}
td {
text-align: center;
max-width: 100%;
min-width: 9%;
}
/*
* Headline Dashboard
*/
.hWelcome {
text-align: center;
color: rgb(52,58,62);
font-size: 100px;
}
.pH_Wel {
color: rgb(52,58,62);
text-align: center;
font-size: 35px;
}
.divH {
background-color: rgba(0, 154, 174,.8);
border-radius: 10px;
box-shadow: 0px 10px 15px rgba(0,82,93,.4);
}
/*
* Produkt Tabelle
*/
#products-selector > label {
vertical-align: middle;
margin: 0 20px 5px 0;
padding-top: 8px;
}
#products-selector > button {
margin-left: 20px;
}
/**
* Benutzerverwaltung
*/
.table-py-2 .btn {
margin: 10px 0;
}
.table-py-2 .btn:last-child {
margin-right: 5px;
}
#calc-page .form-row .info {
background-color: rgba(40, 167, 69, .8);
padding: 5px;
border: 1px rgba(0,0,0,.2) solid;
border-radius: 8px;
}

View File

@ -0,0 +1,17 @@
'use strict';
var $userData = {};
function request(config, done, fail = null) {
if(!config.data) config.data = {};
if($userData.user) config.data.user = $userData.user;
if($userData.pass) config.data.pass = $userData.pass;
if($userData.token) {
config.data.token = $userData.token;
} else {
if(Cookies.get("JWT_SVEN"))
config.data.token = Cookies.get("JWT_SVEN");
}
if(fail) $.ajax(config).done(done).fail(fail);
else $.ajax(config).done(done);
};

View File

@ -0,0 +1,95 @@
'use strict';
// CUSTOM CODE
if($('#calc-page').length) {
var values = {
data: $('#products').val(),
id: $('#products').val().split('|')[0],
price: $('#products').val().split('|')[1],
plt: $('#products').val().split('|')[2],
percentage: {
discount: 7,
cashDiscount: 9,
procurementCosts: 5,
tradingCosts: 4,
profitMargin: 20
},
fields: {},
updateFields: () => {
$('#selected-price').val(values.price);
$('#selected-plt').val(values.plt);
$('#rabatt').val(values.percentage.discount);
$('#skonto').val(values.percentage.cashDiscount);
$('#bezugskosten').val(values.percentage.procurementCosts);
$('#handelskosten').val(values.percentage.tradingCosts);
$('#gewinnzuschlag').val(values.percentage.profitMargin);
values.fields.listeneinkaufspreis = values.price * values.plt; sF('#listeneinkaufspreis');
values.fields.zieleinkaufspreis = gF('#listeneinkaufspreis') - gF('#listeneinkaufspreis') * values.percentage.discount/100; sF('#zieleinkaufspreis');
values.fields.bareinkaufspreis = gF('#zieleinkaufspreis') - gF('#zieleinkaufspreis') * values.percentage.cashDiscount/100; sF('#bareinkaufspreis');
values.fields.bezugspreis = gF('#bareinkaufspreis') - gF('#bareinkaufspreis') * values.percentage.procurementCosts/100; sF('#bezugspreis');
values.fields.selbstkosten = gF('.bezugspreis') + gF('.bezugspreis') * values.percentage.tradingCosts/100; sF('.selbstkosten');
values.fields.angebotspreis = gF('.selbstkosten') + gF('.selbstkosten')*values.percentage.profitMargin/100; sF('#angebotspreis');
values.fields.angebotspreis_mwst = gF('#angebotspreis') * 1.19; sF('#angebotspreis_mwst');
values.fields.angebotspreis_stk = gF('#angebotspreis_mwst') / values.plt; sF('#angebotspreis_stk');
values.fields.rohgewinn = gF('#angebotspreis') - gF('#bezugspreis'); sF('#rohgewinn');
}
};
var pF = (fl) => {return parseFloat(fl)};
var sF = (id) => {
let elem = id;
id = id.substring(1);
if(values.fields[id]) $(elem).val(pF(values.fields[id]).toFixed(2));
};
var gF = (id) => {
id = id.substring(1);
if(values.fields[id]) return values.fields[id]; else return 0;
};
values.updateFields();
$('#products').change((e) => {
values.data = $('#products').val();
values.id = $('#products').val().split('|')[0];
values.price = $('#products').val().split('|')[1];
values.plt = $('#products').val().split('|')[2];
values.updateFields();
});
$('#rabatt').keyup((e) => { values.percentage.discount = $('#rabatt').val(); values.updateFields();});
$('#skonto').keyup((e) => { values.percentage.cashDiscount = $('#skonto').val(); values.updateFields();});
$('#bezugskosten').keyup((e) => { values.percentage.procurementCosts = $('#bezugskosten').val(); values.updateFields();});
$('#handelskosten').keyup((e) => { values.percentage.tradingCosts = $('#handelskosten').val(); values.updateFields();});
$('#gewinnzuschlag').keyup((e) => { values.percentage.profitMargin = $('#gewinnzuschlag').val(); values.updateFields();});
$('#konkurrenzpreis').keypress((e) => {
setTimeout(function () {
if($('#konkurrenzpreis').val().length > 0) {
$('#gewinn').val(pF($('#konkurrenzpreis').val()) - pF(gF('.selbstkosten')));
$('#gewinnPrcnt').val((100/pF(gF('.selbstkosten'))) * (pF($('#konkurrenzpreis').val()) - pF(gF('.selbstkosten'))));
} else {
$('#gewinn').val("0");
$('#gewinnPrcnt').val("0");
}
console.log($('#konkurrenzpreis').val(), gF('.selbstkosten'));
}, 30);
});
let i = 0;
let rgb_values = [];
for (var r = 0; r < 255; r++) {
for (var g = 0; g < 255; g++) {
for (var b = 0; b < 255; b++) {
if(r+g+b >= 255) {
rgb_values.push([r, g, b]);
i++;
}
}
}
if(r === 254 && g === 254 && b == 254) console.log(i);
}
}

View File

@ -0,0 +1,356 @@
/**
* @TODO Documentation
*/
'use strict';
/*
*Ertellen der Produkt Tabelle aus der Datenbank
*/
if (document.getElementById('produktTabelle'&& 'produkte')) {
request({
type:"GET",
url: "/sven/api/article"
}, function (data) {
data.data = data.data.DATA;
var str = "";
var id = 1;
/*
* Erstellt einen String der die Tabelle beinhaltet
*/
str +='<table>';
str +='<thead>';
str +='<tr>';
str +='<th>Produktnummer</th>';
str +='<th>Marke</th>';
str +='<th>Produktname</th>';
str +='<th>Preis</th>';
str +='</tr>';
str +='</thead>';
str +='<tbody>';
data.data.forEach(function(elem) {
//Durchläuft das Array als Schleife
str +='<tr>';
str +='<td>'+elem.ID+'</td>';
str +='<td>'+elem.Marke+'</td>';
str +='<td>'+elem.Name+'</td>';
str +='<td>'+precisionRound(elem.Verkaufspreis, 2)+'€'+'</td>';
str +='</tr>';
});
str +='</tbody>';
str +='</table>';
$('#produktTabelle').html(str); //JQuerry
/*
*Füllt das Produkteingabefeld mit den vorhandenen Produkten
*/
var str_2 = "";
data.data.forEach(function(elem) {
str_2+='<option value='+elem.ID+'>'+elem.Name+'</option>';
});
$('#produkte').html(str_2); //JQuerry
}, function (data) {
console.log(data);
console.log('Error Produkttabelle');
});
};
/*
*Funktion um die Werte von dem ausgewählten Produkt in den Graphen zu bringen
*/
if (document.getElementById('produkte')) {
var produkt_id = null;
$('#produkte').change(function(){
produkt_id = $(this).val()-1;
});
};
var daten_Menge;
function selectProduct(){
if(!produkt_id) return;
request({
url: "/sven/api/statistics"
}, function (data) {
data.data = data.data.DATA;
var series_2 = [];
var labels_2 = [];
data.data.forEach(function(item) {
if(item.f_Artikel_ID === produkt_id) {
labels_2.push(item.Woche);
series_2.push({x: item.Woche, y: item.Menge});
}
});
daten_Menge = {labels: labels_2, series: [series_2]};
if (document.getElementById('chart2')) {
new Chartist.Bar('#chart2', daten_Menge, {
chartPadding: {
top: 0,
right: 0,
bottom: 40,
left: 40
},
plugins: [
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: 'Woche',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: 20
},
textAnchor: 'middle'
},
axisY: {
axisTitle: 'Menge',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: -20
},
textAnchor: 'middle',
flipTitle: false
}
})
]
});
};
}, function (data) {
console.log(data);
console.log('Error Produktgraphen');
});
};
if (document.getElementById('chart2')) {
new Chartist.Bar('#chart2', daten_Menge);
};
/*
* Erstellt den 1. Graphen durch errechen der gesammt Menge die in der spezifischen Woche verkauft wurde
*/
if (document.getElementById('chart1')) {
var gesMengen_Daten;
request({
url: "/sven/api/statistics"
}, function (data) {
data.data = data.data.DATA;
var series_Menge = [];
var labels_Menge = [];
var Wochen = {};
data.data.forEach(function(item) {
item.Woche = parseInt(item.Woche);
if(!Wochen[item.Woche]) Wochen[item.Woche] = parseInt(item.Menge);
else Wochen[item.Woche] += parseInt(item.Menge);
});
Object.keys(Wochen).forEach(function(item) {
series_Menge.push(Wochen[item]);
});
labels_Menge =Object.keys(Wochen);
for(var i=0; i<labels_Menge.lenght;i++)
labels_Menge[i]="Woche"+labels_Menge[i];
var gesMengen_Daten = { labels: labels_Menge, series: [series_Menge]};
new Chartist.Bar('#chart1', gesMengen_Daten, {
chartPadding: {
top: 0,
right: 0,
bottom: 40,
left: 40
},
plugins: [
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: 'Woche',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: 20
},
textAnchor: 'middle'
},
axisY: {
axisTitle: 'Menge',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: -20
},
textAnchor: 'middle',
flipTitle: false
}
})
]
});
}, function (data) {
console.log(data);
console.log('Error Produktgraphen');
});
new Chartist.Bar('#chart1', daten_Menge);
};
/*
* Tabelle vom und fürs Lager
*/
if (document.getElementById('lagerTabelle')) {
request({
type:"GET",
url: "/sven/api/stock",
}, function (data) {
data.data = data.data.DATA;
if(data.data.length == 0) return;
var str = "";
var id = 1;
/*
* Erstellt einen String der die Tabelle beinhaltet
*/
str +='<table>';
str +='<thead>';
str +='<tr>';
str +='<th>Lagerplatz</th>';
str +='<th>Produktname</th>';
str +='<th>Menge</th>';
str +='</tr>';
str +='</thead>';
str +='<tbody>';
data.data.forEach(function(elem) { //Durchläuft das Array als Schleife
str +='<tr>';
str +='<td>'+elem.Lagerplatz+'</td>';
str +='<td>'+elem.f_Artikel_ID+'</td>';
str +='<td>'+elem.Menge+'</td>';
str +='</tr>';
});
str +='</tbody>';
str +='</table>';
$('#lagerTabelle').html(str); //JQuerry
}, function (data) {
console.log(data);
console.log('Error lagerTabelle');
});
};
/*
* Tabelle für erwartete Lieferungen
*/
if (document.getElementById('lieferTabelle')) {
request({
type:"GET",
url: "/sven/api/stockDeliveries"
}, function (data) {
data.data = data.data.DATA;
if(data.data.length == 0) return;
var str = "";
var id = 1;
/*
* Erstellt einen String der die Tabelle beinhaltet
*/
str +='<table>';
str +='<thead>';
str +='<tr>';
str +='<th>Bestellung</th>';
str +='<th>Produktname</th>';
str +='<th>Bestellte Menge</th>';
str +='<th>Ankunftsdatum</th>'
str +='</tr>';
str +='</thead>';
str +='<tbody>';
data.data.forEach(function(elem) { //Durchläuft das Array als Schleife
str +='<tr>';
str +='<td>'+elem.ID+'</td>';
str +='<td>'+elem.f_ProduktID+'</td>';
str +='<td>'+elem.Menge+'</td>';
str +='<td>'+elem.Ankunftsdatum+'</td>';
str +='</tr>';
});
str +='</tbody>';
str +='</table>';
$('#lieferTabelle').html(str);
}, function (data) {
console.log(data);
console.log('Error Lieferungen');
});
};
function precisionRound(number, precision) {
return parseFloat(number).toFixed(precision);
};
function gewinnGraph() {
var gewinn = 0;
if(!produkt_id) return;
request(
{
type:"GET",
url:"/sven/api/article"
}, function(data){
data.data = data.data.DATA;
console.log(data.data);
var series_2 = [];
var labels_2 = [];
gewinn = data.data[produkt_id].Rohgewinn / data.data[produkt_id].Anzahl_pro_Palette;
console.log(produkt_id, data.data[produkt_id].Rohgewinn, data.data[produkt_id].Anzahl_pro_Palette);
request({
type:"GET",
url:"/sven/api/statistics"
}, function(data){
data.data = data.data.DATA;
data.data.forEach(function(item) {
if(item.f_Artikel_ID === produkt_id) {
console.log(item);
labels_2.push(item.Woche);
series_2.push(gewinn);
daten_Menge = {labels: labels_2, series: [series_2]};
if (document.getElementById('chart2')) {
new Chartist.Bar('#chart2', daten_Menge, {
chartPadding: {
top: 0,
right: 0,
bottom: 40,
left: 40
},
plugins: [
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: 'Woche',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: 20
},
textAnchor: 'middle'
},
axisY: {
axisTitle: 'Preis in €',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: -20
},
textAnchor: 'middle',
flipTitle: false
}
})
]
});
}
}
});
});
}, function (data) {
console.log(data);
console.log('Error 12');
});
};

View File

@ -0,0 +1,41 @@
'use strict';
if (document.getElementById('lieferTabelle')) {
request({
type:"GET",
url: "/sven/api/stockDeliveries"
}, function (data) {
data.data = data.data.DATA;
if(data.data.length == 0) return;
var str = "";
var id = 1;
/*
* Erstellt einen String der die Tabelle beinhaltet
*/
str +='<table>';
str +='<thead>';
str +='<tr>';
str +='<th>Bestellung</th>';
str +='<th>Produktname</th>';
str +='<th>Bestellte Menge</th>';
str +='<th>Ankunftsdatum</th>'
str +='</tr>';
str +='</thead>';
str +='<tbody>';
data.data.forEach(function(elem) { //Durchläuft das Array als Schleife
str +='<tr>';
str +='<td>'+elem.ID+'</td>';
str +='<td>'+elem.Name+'</td>';
str +='<td>'+elem.Menge+'</td>';
str +='<td>'+elem.Ankunftsdatum+'</td>';
str +='</tr>';
});
str +='</tbody>';
str +='</table>';
$('#lieferTabelle').html(str);
}, function (data) {
console.log(data);
console.log('Error Lieferungen');
});
};

View File

@ -0,0 +1,48 @@
'use strict';
function precisionRound(number, precision) {
return parseFloat(number).toFixed(precision);
};
/*
*Ertellen der Produkt Tabelle aus der Datenbank
*/
if(document.getElementById("produktTabelle"))
request({
type:"GET",
url: "/sven/api/article"
}, function (data) {
data.data = data.data.DATA;
var str = "";
var id = 1;
/*
* Erstellt einen String der die Tabelle beinhaltet
*/
str +='<table>';
str +='<thead>';
str +='<tr>';
str +='<th>Produktnummer</th>';
str +='<th>Marke</th>';
str +='<th>Produktname</th>';
str +='<th>Preis</th>';
str +='</tr>';
str +='</thead>';
str +='<tbody>';
data.data.forEach(function(elem) {
//Durchläuft das Array als Schleife
str +='<tr>';
str +='<td>'+elem.ID+'</td>';
str +='<td>'+elem.Marke+'</td>';
str +='<td>'+elem.Name+'</td>';
str +='<td>'+precisionRound(elem.Verkaufspreis, 2)+'€'+'</td>';
str +='</tr>';
});
str +='</tbody>';
str +='</table>';
$('#produktTabelle').html(str); //JQuerry
}, function (data) {
console.log(data);
console.log('Error Produkttabelle');
});

View File

@ -0,0 +1,23 @@
'use strict';
$('form[name="login"]').submit(function(e) {
e.preventDefault();
var formData = $('form[name="login"]').serializeArray().reduce(function(obj, item) {
obj[item.name] = item.value;
return obj;
}, {});
request({
url: "/sven/api/auth",
data: {
"type": "login",
"user": formData.loginUser,
"pass": formData.loginPass,
"csrf": formData.csrf_token
}
}, function(data) {
console.log(data.status, data);
document.location = "./";
}, function(data) {
console.log(data.status, data.responseJSON);
});
});

View File

@ -0,0 +1,9 @@
// Bennet Kahrs
'use strict';
$(document).ready(function() {
for (var elem of $('ul.nav > li > a')) {
if(elem.href === window.location.href) {
$(elem).addClass("active");
}
}
});

223
webseite/public/js/stats.js Normal file
View File

@ -0,0 +1,223 @@
'use strict';
var produkt_id = null;
if (document.getElementById('produkte')) {
request({
type:"GET",
url: "/sven/api/article"
}, function (data) {
data.data = data.data.DATA;
/*
*Füllt das Produkteingabefeld mit den vorhandenen Produkten
*/
var str_2 = "";
data.data.forEach(function(elem) {
str_2+='<option value='+elem.ID+'>'+elem.Name+'</option>';
});
$('#produkte').html(str_2); //JQuerry
}, function(data) {});
$('#produkte').change(function(){
produkt_id = $(this).val();
});
};
var gesMengen_Daten;
if (document.getElementById('chart1')) {
request({
url: "/sven/api/salesFigures"
}, function (data) {
data.data = data.data.DATA;
var series_Menge = [];
var labels_Menge = [];
var Wochen = {};
data.data.forEach(function(item) {
item.Woche = parseInt(item.Woche);
if(!Wochen[item.Woche]){
Wochen[item.Woche] = parseInt(item.Menge);
}
else{
Wochen[item.Woche] += parseInt(item.Menge);
}
});
Object.keys(Wochen).forEach(function(item) {
series_Menge.push(Wochen[item]);
});
labels_Menge =Object.keys(Wochen);
gesMengen_Daten = { labels: labels_Menge, series: [series_Menge]};
new Chartist.Bar('#chart1', gesMengen_Daten, {
chartPadding: {
top: 0,
right: 0,
bottom: 40,
left: 40
},
plugins: [
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: 'Woche',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: 40
},
textAnchor: 'middle'
},
axisY: {
axisTitle: 'Menge',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: -20
},
textAnchor: 'middle',
flipTitle: false
}
})
]
});
}, function (data) {
console.log(data);
console.log('Error Produktgraphen');
});
new Chartist.Bar('#chart1', daten_Menge);
};
var daten_Menge;
function selectProduct(){
if(produkt_id === null) return;
request({
url: "/sven/api/salesFigures"
}, function (data) {
data.data = data.data.DATA;
var series_2 = [];
var labels_2 = [];
data.data.forEach(function(item) {
if(item.f_Artikel_ID === produkt_id) {
labels_2.push(item.Woche);
series_2.push({x: item.Woche, y: item.Menge});
}
});
daten_Menge = {labels: labels_2, series: [series_2]};
if (document.getElementById('chart2')) {
new Chartist.Bar('#chart2', daten_Menge, {
chartPadding: {
top: 0,
right: 0,
bottom: 40,
left: 40
},
plugins: [
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: 'Woche',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: 40
},
textAnchor: 'middle'
},
axisY: {
axisTitle: 'Menge',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: -20
},
textAnchor: 'middle',
flipTitle: false
}
})
]
});
};
}, function (data) {
console.log(data);
console.log('Error Produktgraphen');
});
};
if (document.getElementById('chart2')) {
new Chartist.Bar('#chart2', daten_Menge);
};
function gewinnGraph() {
var gewinn = 0;
if(!produkt_id) return;
request({
type:"GET",
url:"/sven/api/article"
}, function(data){
data.data = data.data.DATA;
console.log(data.data);
var series_2 = [];
var labels_2 = [];
gewinn = data.data[produkt_id-1].Rohgewinn / data.data[produkt_id-1].Anzahl_pro_Palette;
console.log(produkt_id, data.data[produkt_id-1].Rohgewinn, data.data[produkt_id-1].Anzahl_pro_Palette);
request({
type:"GET",
url:"/sven/api/salesFigures"
}, function(data){
data.data = data.data.DATA;
data.data.forEach(function(item) {
if(item.f_Artikel_ID === produkt_id) {
console.log(item);
labels_2.push(item.Woche);
series_2.push(gewinn * item.Menge);
}
});
daten_Menge = {labels: labels_2, series: [series_2]};
if (document.getElementById('chart2')) {
new Chartist.Line('#chart2', daten_Menge, {showArea:true}, {
chartPadding: {
top: 0,
right: 0,
bottom: 40,
left: 40
},
plugins: [
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: 'Woche',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: 40
},
textAnchor: 'middle'
},
axisY: {
axisTitle: 'Preis in €',
axisClass: 'ct-axis-title',
offset: {
x: 0,
y: -20
},
textAnchor: 'middle',
flipTitle: false
}
})
]
});
}
}, function (data) {
console.log(data);
console.log('Error 12');
});
})};

View File

@ -0,0 +1,40 @@
'use strict';
if (document.getElementById('lagerTabelle')) {
request({
type:"GET",
url: "/sven/api/stock",
}, function (data) {
data.data = data.data.DATA;
if(data.data.length == 0) return;
var str = "";
var id = 1;
/*
* Erstellt einen String der die Tabelle beinhaltet
*/
str +='<table>';
str +='<thead>';
str +='<tr>';
str +='<th>Lagerplatz</th>';
str +='<th>Produktname</th>';
str +='<th>Menge</th>';
str +='</tr>';
str +='</thead>';
str +='<tbody>';
data.data.forEach(function(elem) { //Durchläuft das Array als Schleife
str +='<tr>';
str +='<td>'+elem.Lagerplatz+'</td>';
str +='<td>'+elem.Name+'</td>';
str +='<td>'+elem.Menge+'</td>';
str +='</tr>';
});
str +='</tbody>';
str +='</table>';
$('#lagerTabelle').html(str); //JQuerry
}, function (data) {
console.log(data);
console.log('Error lagerTabelle');
});
};

View File

@ -0,0 +1,50 @@
'use strict';
if($('#users_page').length) {
$('#modalChangePassword').on('show.bs.modal', function (e) {
let button = $(e.relatedTarget);
let user = button.data('user');
let modal = $(this);
modal.find('.m-cp-span-user').text(user);
modal.find('input#change_username').val(user);
//modal.find('.modal-body input').val(recipient);
});
$('#modalDeleteUser').on('show.bs.modal', function (e) {
let button = $(e.relatedTarget);
let user = button.data('user');
let modal = $(this);
modal.find('.m-du-span-user').text(user);
modal.find('input#del_username').val(user);
//modal.find('.modal-body input').val(recipient);
});
$('#m_cp_form').submit((e) => {
e.preventDefault();
console.log($(this));
});
$('#m_au_form').submit((e) => {
e.preventDefault();
let form = $('#m_au_form');
let formData = FormToObject(form);
request({
url: "/sven/api/auth",
data: {
"type": "login",
"user": formData.username,
"pass": formData.password
}
}, function(data) {
console.log(data.status, data);
document.location = "./";
}, function(data) {
console.log(data.status, data.responseJSON);
});
});
var FormToObject = (elem) => {
let data = elem.serializeArray();
let output = {};
for (var i = 0; i < data.length; i++) {
output[data[i]['name']] = data[i]['value'];
}
return output;
};
}

View File

@ -0,0 +1,44 @@
<?php
// namespace
namespace sven\sys;
/**
* Exception
*
* Custom Exception Handler
*
* @package sven\sys
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
* @see http://php.net/manual/en/class.exception.php <Documentation>
* @TODO Comments
*/
class Exception extends \Exception {
private $title;
public function __construct($title, $message = null, $code = 0, \Exception $previous = null) {
\sven\sys\core::addReplacement("head", "TITLE", "Exception", true);
if(func_get_args()[0] instanceof \Exception) {
$e = func_get_args()[0];
$this->title = get_class($e);
$code = (gettype($e->getCode()) === "integer") ? $e->getCode() : $code;
parent::__construct($e->getMessage(), $code, $e);
} else {
$this->title = $title;
parent::__construct($message, $code, $previous);
}
}
public function getTitle() {
return $this->title;
}
};
?>

36
webseite/sys/autoload.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace sven\sys;
/**
* Autoloader
*
* A simple autoloader that loads class files recursively starting in the directory
* where this class resides.
*
* @todo lel
* @package sven\sys
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
* @see http://php.net/manual/en/language.oop5.autoload.php <Documentation>
*
*/
class Autoloader {
public static function register() {
spl_autoload_register(
function($class) {
$file = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $class).'.class.php';
if(substr($file, 0, 5) == 'sven\\') $file = substr($file, 5);
if(!class_exists($class) && file_exists($file)) {
require $file;
return true;
}
return false;
}
);
}
}
Autoloader::register();
?>

420
webseite/sys/core.class.php Normal file
View File

@ -0,0 +1,420 @@
<?php
// namespace
namespace sven\sys;
/**
* core
*
* This is the core class to connect all classes to a homogeneous system
*
* @package sven\sys
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
* @TODO Comments
*/
class core {
/**
* needLogin
*
* @access private
* @static
* @var boolean
*/
private static $needLogin = false;
/**
* setLogin
*
* sets login access
*
* @access public
* @static
* @param boolean $boolean boolean to set
* @return void
*/
public static function setLogin($boolean) {
self::$needLogin = (boolean) $boolean;
}
/**
* getLogin
*
* @access public
* @static
* @return boolean
*/
public static function getLogin() {
return self::$needLogin;
}
/**
* ////// //////// //////// ////// // // //////// //////// ////////
* // // // // // // // // // // // // //
* ////// // // //////// ////// // // //////// //////// ////////
* // //////// // // // // // // // // // //
* // // // //////// ////// /// // // // // ////////
*/
/**
* exception_categories
*
* Categories which will get imported when an exception was thrown
*
* @access private
* @static
* @var array
*/
private static $exception_categories = ['head', 'header', 'footer', 'foot'];
/**
* exceptions
*
* all thrown exceptions
*
* @access private
* @static
* @var array
*/
private static $exceptions = [];
/**
* templates
*
* template array
*
* [
* 'name' => string, ....
* ]
*
* @access private
* @static
* @var array|null
*/
private static $templates = null;
/**
* replacements
*
* replacements array
*
* [
* 'template' => [
* 'key' => 'value',
* ...
* ],
* ...
* ]
*
* @access private
* @static
* @var array|null
*/
private static $replacements = null;
/**
* configurations
*
* page configuration
*
* [
* 'index' => [ //refers to 'pages/index'
* 'key' => 'value',
* ...
* ],
* ...
* ]
*
* @access private
* @static
* @var array
*/
private static $configurations = [];
/**
* ////// /////// // // // //////// ////// //////
* // // // // // // // // // // // //
* ////// ////// // // // //////// ////// //////
* // // // // // // // // // // //
* // // // // ////// // // // // //////
*/
/**
* apiOutput
*
* api output array (convertable to json and xml)
*
* @access private
* @static
* @var array
*/
private static $apiOutput = [];
/**
* addException
*
* adds an Exception
*
* @access public
* @static
* @param \Exception $exception Throwable Exception
* @return void
*/
public static function addException($exception) {
array_push(self::$exceptions, $exception);
}
/**
* countExceptions
*
* counts Exceptions
*
* @access public
* @static
* @return int
*/
public static function countExceptions() {
return count(self::$exceptions)||0;
}
/**
* getExceptions
*
* gets all Exceptions
*
* @access public
* @static
* @return \Exception[]
*/
public static function getExceptions() {
return self::$exceptions;
}
/**
* getExceptions
*
* adds an Replacement for $category
*
* @access public
* @static
* @param string $category Category
* @param string $text Placeholder
* @param string $replace Replacement
* @return void
*/
public static function addReplacement($category, $text, $replace, $replacePrevious = false) {
$auth = new \sven\sys\security\auth;
if($auth->loggedIn() || (!$auth->loggedIn() && $category !== 'head')) {
if(!is_array(self::$replacements)) self::$replacements = [];
if(!in_array($category, array_keys(self::$replacements))) self::$replacements[$category] = [];
if(!isset(self::$replacements[$category][$text]) || $replacePrevious) self::$replacements[$category][$text] = $replace;
}
}
/**
* getReplacements
*
* gets all Replacements
*
* @access public
* @static
* @return array
*/
public static function getReplacements() {
return self::$replacements;
}
/**
* addTemplate
*
* adds a Template
*
* @access public
* @static
* @param string $category Name
* @param string $string Template
* @param boolean $overwrite overwrite (default=false) (Optional)
* @return boolean State of adding the template
*/
public static function addTemplate($category, $string, $overwrite = false) {
if(!is_array(self::$templates)) self::$templates = [];
if( in_array($category, array_keys(self::$templates)) && $overwrite === true
|| !in_array($category, array_keys(self::$templates))) {
self::$templates[$category] = $string;
return true;
}
return false;
}
/**
* getTemplates
*
* gets all Templates
*
* @access public
* @static
* @return array|null
*/
public static function getTemplates() {
return self::$templates;
}
/**
* getOutput
*
* gets output for the page
*
* @access public
* @static
* @param array $order order or/and templates which get implemented
* @return string
*/
public static function getOutput($order = ['head', 'header', 'exception', 'main', 'footer', 'foot']) {
if(is_array(self::$templates)) {
$auth = new \sven\sys\security\auth;
if(!$auth->loggedIn()) self::$replacements['head']['TITLE'] = 'Please Login';
// replacing with variables
$parsing = "";
$replacements = self::getReplacements();
$replaced_templates = [];
foreach (self::$templates as $key => $value) {
$temp_output = $value;
if(is_array($replacements) && in_array($key, array_keys($replacements))) {
foreach ($replacements[$key] as $item => $data) {
$placeholder = '%%'.$key.'_'.$item.'%%';
if(strpos($temp_output, $placeholder) !== false) {
$temp_output = str_replace($placeholder, $data, $temp_output);
}
}
}
// checking if key is not exception to prevent default template output of exception
if($key !== "exception")
$replaced_templates[$key] = $temp_output;
}
// replacing common replacements ($replacement_category = 'all')
foreach ($replaced_templates as $key => $value) {
if(isset($replacements["all"]) && is_array($replacements["all"])) foreach ($replacements["all"] as $item => $data) {
$placeholder = '%%'.$item.'%%';
if(strpos($value, $placeholder) !== false) {
$replaced_templates[$key] = str_replace($placeholder, $data, $replaced_templates[$key]);
}
}
}
for ($i=0; $i < count($order); $i++) {
if($order[$i] === "main" && !$auth->loggedIn() && self::$needLogin) {
$replaced_templates["main"] = self::getTemplates()["login_msg"];
}
// Exception handling if exceptions were thrown
if($order[$i] === "exception" && self::getExceptions()) {
$replaced_templates["exception"] = "";
$error_template = self::getTemplates()['exception'];
ob_start();
$startTag = "%%repeat_START%%";
$endTag = "%%repeat_END%%";
$start = strpos($error_template, $startTag);
$end = strpos($error_template, $endTag);
$template = \sven\sys\sven\utilities::getBetween($error_template, $startTag, $endTag);
foreach (self::getExceptions() as $exception) {
$tempException = $template;
$tempException = str_replace('%%exception_title%%', trim($exception->getTitle(), "\n\r\t"), $tempException);
$tempException = str_replace('%%exception_code%%', trim($exception->getCode(), "\n\r\t"), $tempException);
$tempException = str_replace('%%exception_message%%', trim($exception->getMessage(), "\n\r\t"), $tempException);
print_r($tempException);
}
$data = ob_get_contents();
ob_end_clean();
//$data = substr($data, 0, strlen($endTag));
//replace the normal main part
$replaced_templates["exception"] .= "\n".substr_replace($error_template, $data, $start, $end-$start+strlen($endTag));
}
//adding replaced content to output
if(in_array($order[$i], array_keys($replaced_templates))) {
$parsing .= $replaced_templates[$order[$i]]."\n";
}
}
return $parsing;
}
//no templates found
else return "ERROR 500 - \sven\sys\core::getOutput()";
}
/**
* rawApiOutput
*
* returns self::$apiOutput
*
* @access public
* @static
* @return mixed[]
*/
public static function rawApiOutput() {
return self::$apiOutput;
}
/**
* replaceApiOutput
*
* replaces api output with $api
*
* @access public
* @static
* @param mixed[] $api api array
*/
public static function replaceApiOutput($api) {
if($api === null || $api === false) {
$api = (object) [
"msg" => "Request method not supported. See README.md for supported methods",
"error" => ["REQUEST_METHOD_NOT_SUPPORTED"]
];
}
self::$apiOutput = $api;
}
/**
* getApiOutput
*
* gets output for api usage
*
* @access public
* @static
* @param string $type "json" or "xml"
* @return string
*/
public static function getApiOutput($type='json') {
if(!isset(self::$apiOutput->state)) self::$apiOutput->state = "failed";
if(self::getExceptions()) {
self::$apiOutput->state = "failed";
self::$apiOutput->exceptions = [];
foreach (self::getExceptions() as $exception) {
$arr = [
"title" => $exception->getTitle(),
"message" => $exception->getMessage(),
"code" => $exception->getCode()
];
self::$apiOutput->exceptions[] = $arr;
}
}
if(self::$apiOutput->access === "denied" && self::$apiOutput->state === "failed") http_response_code(401);
elseif(self::$apiOutput->state === "failed") http_response_code(400);
if($type == "json") {
header("Content-Type: application/json");
return json_encode(self::$apiOutput, JSON_PRETTY_PRINT);
}
}
}
?>

View File

@ -0,0 +1,171 @@
<?php
// namespace
namespace sven\sys\mysql;
// required library
$dir = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, dirname(__FILE__)."/../sources/envms-fluentpdo/src/");
if(file_exists($dir)) {
//prebuilt directory files to serve classes faster
$files = [
$dir.'Query.php', $dir.'Literal.php', $dir.'Structure.php', $dir.'Utilities.php', $dir.'Queries\Base.php', $dir.'Queries\Common.php', $dir.'Queries\Delete.php', $dir.'Queries\Insert.php', $dir.'Queries\Select.php', $dir.'Queries\Update.php'
];
foreach ($files as $file) {
if(file_exists($file))
require_once($file);
else
return \sven\sys\core::addException(new \sven\sys\Exception("ERROR: Files not found", "SQL Query Builder was not found ({$file})", 404));
}
}
else return \sven\sys\core::addException(new \sven\sys\Exception("ERROR: Files not found", "SQL Query Builder was not found", 404));
unset($dir);
/**
* mysql
*
* mysql.class uses PDO to connect to our database and handle requests
* and a SQL Query Builder to get some handsome code
*
* Example usage:
* $db = new \sven\sys\mysql();
* $output = $db->select('*', 'users', 'ID=4');
* var_dump($output);
*
* @package sven\sys\mysql
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
* @see https://github.com/envms/fluentpdo <query builder>
* @see http://php.net/manual/de/class.pdo.php <PDO>
*
*/
class mysql {
/**
* instance
*
* Instance
*
* @access private
* @static
* @var resource (new self();)
*/
private static $instance = null;
/**
* connection
*
* PDO Connection
*
* @access private
* @static
* @var object|null Should contain the database connection
*/
private static $connection = null;
/**
* __construct
*
* Class constructor
*
* @access public
* @return void
*/
public function __construct() {
self::$instance = $this;
$this->connect();
}
/**
* __destruct
*
* Class destructor
*
* @access public
* @return void
*/
public function __destruct() { $this->disconnect(); }
/**
* getInstance
*
* returns Instance
*
* @access public
* @return object
*/
public static function getInstance() {
return self::$instance;
}
/**
* getBuilder
*
* returns SQL Builder to query
*
* @access public
* @return \Envms\FluentPDO\Query|\sven\sys\sven\uncallable
*/
public function getBuilder() {
if(self::$connection !== null) return new \Envms\FluentPDO\Query(self::$connection);
else {
\sven\sys\core::addException(new \sven\sys\Exception("Exception", "Error: MySQL \$connection is NULL", 2002));
return new \sven\sys\sven\uncallable;
}
}
/**
* connect
*
* connects to our database
*
* @access private
* @return void
*/
private function connect() {
if(self::$connection) return;
if(php_uname('r') === "6.1") {
//if(false) {
// development place @ school
$data = (object) [
'host' => '102-012',
'user' => 'intabi19',
'pass' => 'hallo',
'db' => 'sven'
];
} else {
// development place @ home
$data = (object) [
'host' => '127.0.0.1',
'user' => 'root',
'pass' => '',
'db' => 'sven'
];
}
try {
if(mysqli_connect($data->host, $data->user, $data->pass)) {
self::$connection = new \PDO('mysql:host='.$data->host.';dbname='.$data->db.';charset=utf8mb4', $data->user, $data->pass);
self::$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
self::$connection->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
}
}
catch (\Exception $e) {
\sven\sys\core::addException(new \sven\sys\Exception($e));
}
}
/**
* disconnect
*
* closes connection
*
* @access private
* @return void
*/
private function disconnect() {
if(gettype(self::$connection) == "object") self::$connection = null;
}
};
?>

View File

@ -0,0 +1,149 @@
<?php
// namespace
namespace sven\sys\security;
use \sven\sys\sven as sven;
use \sven\sys\mysql\mysql as mysql;
use \sven\sys\core as CORE;
use \Lcobucci\JWT\Builder as JWT_Builder;
use \Lcobucci\JWT\Signer\Hmac\Sha256 as JWT_Sha256;
use \Lcobucci\JWT\ValidationData as JWT_Validation;
use \Lcobucci\JWT\Parser as JWT_Parser;
$dir = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, dirname(__FILE__)."/../sources/jwt/src/");
if(file_exists($dir)) {
$files = [
$dir.'Claim.php', $dir.'Claim\Validatable.php', $dir.'Parser.php', $dir.'Builder.php', $dir.'Signer.php', $dir.'Token.php', $dir.'ValidationData.php', $dir.'Claim\Basic.php', $dir.'Claim\EqualsTo.php', $dir.'Claim\Factory.php', $dir.'Claim\GreaterOrEqualsTo.php', $dir.'Claim\LesserOrEqualsTo.php',
$dir.'Parsing\Decoder.php', $dir.'Parsing\Encoder.php', $dir.'Signature.php', $dir.'Signer\BaseSigner.php',
$dir.'Signer\Ecdsa\KeyParser.php', $dir.'Signer\Ecdsa.php', $dir.'Signer\Ecdsa\Sha256.php', $dir.'Signer\Ecdsa\Sha384.php', $dir.'Signer\Ecdsa\Sha512.php',
$dir.'Signer\Hmac.php', $dir.'Signer\Hmac\Sha256.php', $dir.'Signer\Hmac\Sha384.php', $dir.'Signer\Hmac\Sha512.php',
$dir.'Signer\Key.php', $dir.'Signer\Keychain.php',
$dir.'Signer\Rsa.php', $dir.'Signer\Rsa\Sha256.php', $dir.'Signer\Rsa\Sha384.php', $dir.'Signer\Rsa\Sha512.php'
];
foreach ($files as $file) {
if(file_exists($file)) {
require_once($file);
} else
return \sven\sys\core::addException(new \sven\sys\Exception("ERROR: Files not found", "JWT Library was not found ({$file})", 404));
}
} else {
return \sven\sys\core::addException(new \sven\sys\Exception("ERROR: Files not found", "JWT Library was not found", 404));
}
unset($dir);
/**
* auth
*
* Authentication class
*
* @package sven\sys\security
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
* @TODO Documentation
*
*/
class auth {
private const TOKEN_SIGNATURE = "5641189c6596892b8b03a8d939803747";
public function __construct() {
$mysql = new mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) === "sven\sys\sven\uncallable") {
CORE::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));
}
}
public function loggedIn() {
$cookie = new cookie();
$body = \sven\sys\sven\web::getRequestBody();
$token = ($cookie->read("JWT_SVEN") ? $cookie->read("JWT_SVEN") : null);
if($token) {
if($cookie->read("JWT_SVEN") || $token) {
$token = (new JWT_Parser())->parse((string) $token);
$signer = new JWT_Sha256;
return $token->verify($signer, self::TOKEN_SIGNATURE);
}
} else {
if(isset($user) && isset($pass))
return $this->verifyLogin($user, $pass);
}
return false;
}
public function verifyLogin($username, $password) {
$mysql = new mysql();
$fluent = $mysql->getBuilder();
if(get_class($fluent) !== "sven\sys\sven\uncallable") {
$query = $fluent->from('t_benutzer')->select('t_benutzer.*')->where("Name = ?", $username);
$data = $query->fetch();
if($data && $this->verifyPassword($password, $data->Passwort)) {
return true;
}
} else {
CORE::addException(new \sven\sys\Exception("\sven\sys\sven\uncallable", "Can't find/use FluentPDO"));
}
return false;
}
public function login($username) {
$session = new session();
$session->write('auth_username', $username);
$session->write('auth_ip', $_SERVER['REMOTE_ADDR']);
$signer = new JWT_Sha256;
$time = time();
$token = (new JWT_Builder())->setIssuer('https://rxbn.de')
->setAudience('http://eps.local')
->setId('4f1g26a1aba', true)
->setIssuedAt($time)
->setNotBefore($time+60)
->setExpiration($time+60*60)
->set('username', $username)
->sign($signer, self::TOKEN_SIGNATURE)
->getToken();
$cookie = new cookie();
$cookie->write('JWT_SVEN', (string) $token);
return (string) $token;
}
public function logout() {
$cookie = new cookie();
$cookie->drop();
$session = new session();
$session->drop();
}
public function createCSRF($form_name) {
$token = bin2hex(random_bytes(32));
$session = new session();
$session->write('csrf_form_'.$form_name, $token);
return $token;
}
public function validateCSRF($form_name, $token) {
$session = new session();
return hash_equals($session->read('csrf_form_'.$form_name), $token);
}
public function hashPassword($password) {
return password_hash($password, PASSWORD_DEFAULT, ["cost" => 12]);
}
public function verifyPassword($password, $hash) {
return password_verify($password, $hash);
}
public function getSomething(){}
};
?>

View File

@ -0,0 +1,91 @@
<?php
// namespace
namespace sven\sys\security;
/**
* cookie
*
* Wrapping cookie functions
*
* @package sven\sys\security
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
*
*/
class cookie {
public const EXPIRE_TIME = 1800; // in seconds (30mins)
/**
* destructor
*/
public function __destruct() {
$cks = $_COOKIE;
foreach ($cks as $key => $value) {
$this->write($key, $value, time() + self::EXPIRE_TIME);
}
}
/**
* write
*
* create a cookie with a specific value
*
* @access public
* @param string $key Cookie name
* @param string $value Cookie value
* @param int $expire Expiring timestamp as UNIX timestamp (2018/12/15 12:14:13 => 1544876053) (0 => cookie will be deleted with session end)
* @param string $path URL
* @param string $domain Domain/Subdomain name where the cookie will be accessable
* @param boolean $secure HTTP Secure Flag
* @param boolean $httponly Unreadability for Javascript (XSS Security)
* @return void
*/
public function write($key, $value = "", $expire = 0, $path = "/", $domain = null, $secure = false, $httponly = false) {
setcookie($key, $value, $expire, $path, $domain, $secure, $httponly);
}
/**
* read
*
* read a cookie
*
* @access public
* @param string $key Cookie name
* @return string|boolean
*/
public function read($key) {
return (isset($_COOKIE[$key]) ? $_COOKIE[$key]: false);
}
/**
* remove
*
* remove a cookie
*
* @access public
* @param string $key Cookie name
* @return void
*/
public function remove($key) {
unset($_COOKIE[$key]);
$this->write($key, "", time()-3600);
}
/**
* drop
*
* drops all cookies
*
* @access public
* @return void
*/
public function drop() {
foreach ($_COOKIE as $key => $value) {
$this->remove($key);
}
}
};
?>

View File

@ -0,0 +1,42 @@
<?php
// namespace
namespace sven\sys\security;
/**
* session
*
* Wrapping session functions
*
* @package sven\sys\security
* @copyright 2018 Ruben Meyer
* @author Ruben Meyer <contact@rxbn.de>
* @version 0.1.0
* @TODO Documentation
*/
class session {
public static $id;
public function __construct() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
}
public function write($key, $value) {
$_SESSION[$key] = $value;
}
public function read($key) {
if(isset($_SESSION[$key]))
return $_SESSION[$key];
else return false;
}
public function remove($key) {
unset($_SESSION[$key]);
}
public function drop() {
session_destroy();
}
};
?>

View File

@ -0,0 +1 @@
/vendor/

View File

@ -0,0 +1,18 @@
language: php
php:
- 7.0
- 7.1
- 7.2
- hhvm
script: php tests/run-tests.php
before_script:
- mysql -u root < tests/fluentdb.sql
notifications:
slack:
secure: cyZnqOCV/gO9p23Z8Lr0e4sc3TqXi0v+VQ8neeRTNalYuiwgn9Co1NakCBO7yyku6qyWE9EOaypYBJlZgaLExLAyCGmaSTRduLlE7P1bdcNnkmns0ikoenFzXd5Uq26ExsegGzUGSbjwtzVhiHLUwigPsJNpnwsMOa2Co5ieo04=
os: linux
group: stable
dist: trusty

View File

@ -0,0 +1,21 @@
{
"name": "envms/fluentpdo",
"description": "FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.",
"keywords": ["db", "database", "dbal", "pdo", "fluent", "query", "builder", "mysql", "oracle"],
"homepage": "https://github.com/envms/fluentpdo",
"license": ["Apache-2.0", "GPL-2.0+"],
"authors": [
{
"name": "envms",
"homepage": "http://env.ms"
}
],
"autoload": {
"psr-4": {
"Envms\\FluentPDO\\": "src/"
}
},
"require-dev": {
"phpunit/phpunit": "^6.2"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
# FluentPDO [![Build Status](https://secure.travis-ci.org/envms/fluentpdo.png?branch=master)](http://travis-ci.org/envms/fluentpdo) [![Maintainability](https://api.codeclimate.com/v1/badges/19210ca91c7055b89705/maintainability)](https://codeclimate.com/github/fpdo/fluentpdo/maintainability)
FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.
## Features
- Easy interface for creating queries step by step
- Support for any database compatible with PDO
- Simple API based on PDO and SQL syntax
- Ability to build complex SELECT, INSERT, UPDATE & DELETE queries with little code
- Small and very fast
- Type hinting for magic methods with code completion in smart IDEs
## Requirements
The latest (2.x) release of FluentPDO supports PHP 7.0, 7.1 and 7.2
The legacy (1.x) release of FluentPDO supports PHP 5.4 to 7.1
## Reference
[Sitepoint - Getting Started with FluentPDO](http://www.sitepoint.com/getting-started-fluentpdo/)
## Install
### Composer
The preferred way to install FluentPDO is via [composer](http://getcomposer.org/). v1.1.x will be the last until the release of 2.0, so we recommend using 1.1.* to ensure no breaking changes are introduced.
Add the following line in your `composer.json` file:
"require": {
...
"envms/fluentpdo": "1.1.*"
}
update your dependencies with `composer update`, and you're done!
### Copy
If you prefer not to use composer, simply copy the `/FluentPDO` directory into your libraries directory and add:
```php
include "[your-library-directory]/FluentPDO/FluentPDO.php";
```
to the top of your application.
## Getting Started
Create a new PDO instance, and pass the instance to FluentPDO:
```php
$pdo = new PDO("mysql:dbname=fluentdb", "root");
$fluent = new FluentPDO($pdo);
```
Then, creating queries is quick and easy:
```php
$query = $fluent->from('article')
->where('published_at > ?', $date)
->orderBy('published_at DESC')
->limit(5);
```
which builds the query below:
```mysql
SELECT article.*
FROM article
WHERE published_at > ?
ORDER BY published_at DESC
LIMIT 5
```
To get data from the select, all we do is loop through the returned array:
```php
foreach ($query as $row) {
echo "$row[title]\n";
}
```
## Using the Smart Join Builder
Let's start with a traditional join, below:
```php
$query = $fluent->from('article')
->leftJoin('user ON user.id = article.user_id')
->select('user.name');
```
That's pretty verbose, and not very smart. If your tables use proper primary and foreign key names, you can shorten the above to:
```php
$query = $fluent->from('article')
->leftJoin('user')
->select('user.name');
```
That's better, but not ideal. However, it would be even easier to **not write any joins**:
```php
$query = $fluent->from('article')
->select('user.name');
```
Awesome, right? FluentPDO is able to build the join for you, by you prepending the foreign table name to the requested column.
All three snippets above will create the exact same query:
```mysql
SELECT article.*, user.name
FROM article
LEFT JOIN user ON user.id = article.user_id
```
## CRUD Query Examples
##### SELECT
```php
$query = $fluent->from('article')->where('id', 1);
$query = $fluent->from('user', 1); // shorter version if selecting one row by primary key
```
##### INSERT
```php
$values = array('title' => 'article 1', 'content' => 'content 1');
$query = $fluent->insertInto('article')->values($values)->execute();
$query = $fluent->insertInto('article', $values)->execute(); // shorter version
```
##### UPDATE
```php
$set = array('published_at' => new FluentLiteral('NOW()'));
$query = $fluent->update('article')->set($set)->where('id', 1)->execute();
$query = $fluent->update('article', $set, 1)->execute(); // shorter version if updating one row by primary key
```
##### DELETE
```php
$query = $fluent->deleteFrom('article')->where('id', 1)->execute();
$query = $fluent->deleteFrom('article', 1)->execute(); // shorter version if deleting one row by primary key
```
***Note**: INSERT, UPDATE and DELETE queries will only run after you call `->execute()`*
Full documentation can be found on the [FluentPDO homepage](http://envms.github.io/fluentpdo/)
## License
Free for commercial and non-commercial use under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) or [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html) licenses.

View File

@ -0,0 +1,31 @@
<?php
namespace Envms\FluentPDO;
/**
* SQL literal value
*/
class Literal
{
/** @var string */
protected $value = '';
/**
* Create literal value
*
* @param string $value
*/
function __construct($value) {
$this->value = $value;
}
/**
* Get literal value
*
* @return string
*/
function __toString() {
return $this->value;
}
}

View File

@ -0,0 +1,387 @@
<?php
namespace Envms\FluentPDO\Queries;
use Envms\FluentPDO\{Query,Literal,Structure,Utilities};
/**
* Base query builder
*/
abstract class Base implements \IteratorAggregate
{
/** @var Query */
private $fluent;
/** @var \PDOStatement */
private $result;
/** @var float */
private $time;
/** @var bool */
private $object = false;
/** @var array - definition clauses */
protected $clauses = array();
/** @var array */
protected $statements = array();
/** @var array */
protected $parameters = array();
/**
* BaseQuery constructor.
*
* @param Query $fluent
* @param $clauses
*/
protected function __construct(Query $fluent, $clauses) {
$this->fluent = $fluent;
$this->clauses = $clauses;
$this->initClauses();
}
/**
* Return formatted query when request class representation
* ie: echo $query
*
* @return string - formatted query
*/
public function __toString() {
return $this->getQuery();
}
/**
* Initialize statement and parameter clauses.
*/
private function initClauses() {
foreach ($this->clauses as $clause => $value) {
if ($value) {
$this->statements[$clause] = array();
$this->parameters[$clause] = array();
} else {
$this->statements[$clause] = null;
$this->parameters[$clause] = null;
}
}
}
/**
* Add statement for all kind of clauses
*
* @param $clause
* @param $statement
* @param array $parameters
*
* @return $this
*/
protected function addStatement($clause, $statement, $parameters = array()) {
if ($statement === null) {
return $this->resetClause($clause);
}
if ($this->clauses[$clause]) {
if (is_array($statement)) {
$this->statements[$clause] = array_merge($this->statements[$clause], $statement);
} else {
$this->statements[$clause][] = $statement;
}
$this->parameters[$clause] = array_merge($this->parameters[$clause], $parameters);
} else {
$this->statements[$clause] = $statement;
$this->parameters[$clause] = $parameters;
}
return $this;
}
/**
* Remove all prev defined statements
*
* @param $clause
*
* @return $this
*/
protected function resetClause($clause) {
$this->statements[$clause] = null;
$this->parameters[$clause] = array();
if (isset($this->clauses[$clause]) && $this->clauses[$clause]) {
$this->statements[$clause] = array();
}
return $this;
}
/**
* Implements method from IteratorAggregate
*
* @return \PDOStatement
*/
public function getIterator() {
return $this->execute();
}
/**
* Execute query with earlier added parameters
*
* @return \PDOStatement
*/
public function execute() {
$query = $this->buildQuery();
$parameters = $this->buildParameters();
$result = $this->fluent->getPdo()->prepare($query);
// At this point, $result is a PDOStatement instance, or false.
// PDO::prepare() does not reliably return errors. Some database drivers
// do not support prepared statements, and PHP emulates them. Postgresql
// does support prepared statements, but PHP does not call Postgresql's
// prepare function until we call PDOStatement::execute() below.
// If PDO::prepare() worked properly, this is where we would check
// for prepare errors, such as invalid SQL.
if ($this->object !== false) {
if (class_exists($this->object)) {
$result->setFetchMode(\PDO::FETCH_CLASS, $this->object);
} else {
$result->setFetchMode(\PDO::FETCH_OBJ);
}
} elseif ($this->fluent->getPdo()->getAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE) == \PDO::FETCH_BOTH) {
$result->setFetchMode(\PDO::FETCH_ASSOC);
}
$time = microtime(true);
if ($result && $result->execute($parameters)) {
$this->time = microtime(true) - $time;
} else {
$result = false;
}
$this->result = $result;
$this->debugger();
return $result;
}
/**
* Echo/pass a debug string
*/
private function debugger() {
if ($this->fluent->debug) {
if (!is_callable($this->fluent->debug)) {
$backtrace = '';
$query = $this->getQuery();
$parameters = $this->getParameters();
$debug = '';
if ($parameters) {
$debug = '# parameters: ' . implode(', ', array_map(array($this, 'quote'), $parameters)) . "\n";
}
$debug .= $query;
$pattern = '(^' . preg_quote(__DIR__) . '(\\.php$|[/\\\\]))'; // can be static
foreach (debug_backtrace() as $backtrace) {
if (isset($backtrace['file']) && !preg_match($pattern, $backtrace['file'])) {
// stop on first file outside Query source codes
break;
}
}
$time = sprintf('%0.3f', $this->time * 1000) . ' ms';
$rows = ($this->result) ? $this->result->rowCount() : 0;
$finalString = "# $backtrace[file]:$backtrace[line] ($time; rows = $rows)\n$debug\n\n";
if (defined('STDERR')) { // if STDERR is set, send there, otherwise just output the string
if (is_resource(STDERR)) {
fwrite(STDERR, $finalString);
}
else {
echo $finalString;
}
}
else {
echo $finalString;
}
} else {
call_user_func($this->fluent->debug, $this);
}
}
}
/**
* @return \PDO
*/
protected function getPDO() {
return $this->fluent->getPdo();
}
/**
* @return Structure
*/
protected function getStructure() {
return $this->fluent->getStructure();
}
/**
* Get PDOStatement result
*
* @return \PDOStatement
*/
public function getResult() {
return $this->result;
}
/**
* Get time of execution
*
* @return float
*/
public function getTime() {
return $this->time;
}
/**
* Get query parameters
*
* @return array
*/
public function getParameters() {
return $this->buildParameters();
}
/**
* Get query string
*
* @param bool $formatted - Return formatted query
*
* @return string
*/
public function getQuery($formatted = true) {
$query = $this->buildQuery();
if ($formatted) {
$query = Utilities::formatQuery($query);
}
return $query;
}
/**
* Generate query
*
* @return string
* @throws \Exception
*/
protected function buildQuery() {
$query = '';
foreach ($this->clauses as $clause => $separator) {
if ($this->clauseNotEmpty($clause)) {
if (is_string($separator)) {
$query .= " $clause " . implode($separator, $this->statements[$clause]);
} elseif ($separator === null) {
$query .= " $clause " . $this->statements[$clause];
} elseif (is_callable($separator)) {
$query .= call_user_func($separator);
} else {
throw new \Exception("Clause '$clause' is incorrectly set to '$separator'.");
}
}
}
return trim($query);
}
/**
* @param $clause
*
* @return bool
*/
private function clauseNotEmpty($clause) {
if ((Utilities::isCountable($this->statements[$clause])) && $this->clauses[$clause]) {
return (boolean)count($this->statements[$clause]);
} else {
return (boolean)$this->statements[$clause];
}
}
/**
* @return array
*/
protected function buildParameters() {
$parameters = array();
foreach ($this->parameters as $clauses) {
if (is_array($clauses)) {
foreach ($clauses as $value) {
if (is_array($value) && is_string(key($value)) && substr(key($value), 0, 1) == ':') {
// this is named params e.g. (':name' => 'Mark')
$parameters = array_merge($parameters, $value);
}
else {
$parameters[] = $value;
}
}
}
else {
if ($clauses) {
$parameters[] = $clauses;
}
}
}
return $parameters;
}
/**
* @param $value
*
* @return string
*/
protected function quote($value) {
if (!isset($value)) {
return "NULL";
}
if (is_array($value)) { // (a, b) IN ((1, 2), (3, 4))
return "(" . implode(", ", array_map(array($this, 'quote'), $value)) . ")";
}
$value = $this->formatValue($value);
if (is_float($value)) {
return sprintf("%F", $value); // otherwise depends on setlocale()
}
if ($value === false) {
return "0";
}
if (is_int($value) || $value instanceof Literal) { // number or SQL code - for example "NOW()"
return (string)$value;
}
return $this->fluent->getPdo()->quote($value);
}
/**
* @param \DateTime $val
*
* @return string
*/
private function formatValue($val) {
if ($val instanceof \DateTime) {
return $val->format("Y-m-d H:i:s"); // may be driver specific
}
return $val;
}
/**
* Select an item as object
*
* @param boolean|object $object If set to true, items are returned as stdClass, otherwise a class
* name can be passed and a new instance of this class is return.
* Can be set to false to return items as an associative array.
*
* @return Base
*/
public function asObject($object = true) {
$this->object = $object;
return $this;
}
}

View File

@ -0,0 +1,329 @@
<?php
namespace Envms\FluentPDO\Queries;
use Envms\FluentPDO\{Literal,Utilities};
/**
* CommonQuery add JOIN and WHERE clauses for (SELECT, UPDATE, DELETE)
*/
abstract class Common extends Base
{
/** @var array - methods which are allowed to be call by the magic method __call() */
private $validMethods = ['from', 'fullJoin', 'group', 'groupBy', 'having', 'innerJoin', 'join', 'leftJoin',
'limit', 'offset', 'order', 'orderBy', 'outerJoin', 'rightJoin', 'select'];
/** @var array - Query tables (also include table from clause FROM) */
protected $joins = array();
/** @var bool - Disable adding undefined joins to query? */
protected $isSmartJoinEnabled = true;
/**
* @return $this
*/
public function enableSmartJoin() {
$this->isSmartJoinEnabled = true;
return $this;
}
/**
* @return $this
*/
public function disableSmartJoin() {
$this->isSmartJoinEnabled = false;
return $this;
}
/**
* @return bool
*/
public function isSmartJoinEnabled() {
return $this->isSmartJoinEnabled;
}
/**
* Add where condition, more calls appends with AND
*
* @param string $condition possibly containing ? or :name (PDO syntax)
* @param mixed $parameters array or a scalar value
*
* @return Common
*/
public function where($condition, $parameters = array()) {
if ($condition === null) {
return $this->resetClause('WHERE');
}
if (!$condition) {
return $this;
}
if (is_array($condition)) { // where(array("column1" => 1, "column2 > ?" => 2))
foreach ($condition as $key => $val) {
$this->where($key, $val);
}
return $this;
}
$args = func_get_args();
if (count($args) == 1) {
return $this->addStatement('WHERE', $condition);
}
/*
Check that there are 2 arguments, a condition and a parameter value. If the condition contains
a parameter, add them; it's up to the dev to be valid sql. Otherwise it's probably
just an identifier, so construct a new condition based on the passed parameter value.
*/
if (count($args) == 2 && !preg_match('/(\?|:\w+)/i', $condition)) {
// condition is column only
if (is_null($parameters)) {
return $this->addStatement('WHERE', "$condition is NULL");
} elseif ($args[1] === array()) {
return $this->addStatement('WHERE', 'FALSE');
} elseif (is_array($args[1])) {
$in = $this->quote($args[1]);
return $this->addStatement('WHERE', "$condition IN $in");
}
// don't parameterize the value if it's an instance of Literal
if ($parameters instanceof Literal) {
$condition = "{$condition} = {$parameters}";
return $this->addStatement('WHERE', $condition);
}
else {
$condition = "$condition = ?";
}
}
array_shift($args);
return $this->addStatement('WHERE', $condition, $args);
}
/**
* @param string $name
* @param array $parameters - first is $statement followed by $parameters
*
* @return $this|Select
*/
public function __call($name, $parameters = array()) {
if (!in_array($name, $this->validMethods)) {
trigger_error("Call to invalid method " . get_class($this) . "::{$name}()", E_USER_ERROR);
}
$clause = Utilities::toUpperWords($name);
if ($clause == 'GROUP') {
$clause = 'GROUP BY';
}
if ($clause == 'ORDER') {
$clause = 'ORDER BY';
}
if ($clause == 'FOOT NOTE') {
$clause = "\n--";
}
$statement = array_shift($parameters);
if (strpos($clause, 'JOIN') !== false) {
return $this->addJoinStatements($clause, $statement, $parameters);
}
return $this->addStatement($clause, $statement, $parameters);
}
/**
* @return string
*/
protected function getClauseJoin() {
return implode(' ', $this->statements['JOIN']);
}
/**
* Statement can contain more tables (e.g. "table1.table2:table3:")
*
* @param $clause
* @param $statement
* @param array $parameters
*
* @return $this|Select
*/
private function addJoinStatements($clause, $statement, $parameters = array()) {
if ($statement === null) {
$this->joins = array();
return $this->resetClause('JOIN');
}
if (array_search(substr($statement, 0, -1), $this->joins) !== false) {
return $this;
}
// match "table AS alias"
preg_match('/`?([a-z_][a-z0-9_\.:]*)`?(\s+AS)?(\s+`?([a-z_][a-z0-9_]*)`?)?/i', $statement, $matches);
$joinAlias = '';
$joinTable = '';
if ($matches) {
$joinTable = $matches[1];
if (isset($matches[4]) && !in_array(strtoupper($matches[4]), array('ON', 'USING'))) {
$joinAlias = $matches[4];
}
}
if (strpos(strtoupper($statement), ' ON ') || strpos(strtoupper($statement), ' USING')) {
if (!$joinAlias) {
$joinAlias = $joinTable;
}
if (in_array($joinAlias, $this->joins)) {
return $this;
} else {
$this->joins[] = $joinAlias;
$statement = " $clause $statement";
return $this->addStatement('JOIN', $statement, $parameters);
}
}
// $joinTable is list of tables for join e.g.: table1.table2:table3....
if (!in_array(substr($joinTable, -1), array('.', ':'))) {
$joinTable .= '.';
}
preg_match_all('/([a-z_][a-z0-9_]*[\.:]?)/i', $joinTable, $matches);
$mainTable = '';
if (isset($this->statements['FROM'])) {
$mainTable = $this->statements['FROM'];
} elseif (isset($this->statements['UPDATE'])) {
$mainTable = $this->statements['UPDATE'];
}
$lastItem = array_pop($matches[1]);
array_push($matches[1], $lastItem);
foreach ($matches[1] as $joinItem) {
if ($mainTable == substr($joinItem, 0, -1)) {
continue;
}
$alias = '';
if ($joinItem == $lastItem) {
$alias = $joinAlias; // use $joinAlias only for $lastItem
}
$newJoin = $this->createJoinStatement($clause, $mainTable, $joinItem, $alias);
if ($newJoin) {
$this->addStatement('JOIN', $newJoin, $parameters);
}
$mainTable = $joinItem;
}
return $this;
}
/**
* Create join string
*
* @param $clause
* @param $mainTable
* @param $joinTable
* @param string $joinAlias
*
* @return string
*/
private function createJoinStatement($clause, $mainTable, $joinTable, $joinAlias = '') {
if (in_array(substr($mainTable, -1), array(':', '.'))) {
$mainTable = substr($mainTable, 0, -1);
}
$referenceDirection = substr($joinTable, -1);
$joinTable = substr($joinTable, 0, -1);
$asJoinAlias = '';
if ($joinAlias) {
$asJoinAlias = " AS $joinAlias";
} else {
$joinAlias = $joinTable;
}
if (in_array($joinAlias, $this->joins)) { // if the join exists don't create it again
return '';
} else {
$this->joins[] = $joinAlias;
}
if ($referenceDirection == ':') { // back reference
$primaryKey = $this->getStructure()->getPrimaryKey($mainTable);
$foreignKey = $this->getStructure()->getForeignKey($mainTable);
return " $clause $joinTable$asJoinAlias ON $joinAlias.$foreignKey = $mainTable.$primaryKey";
} else {
$primaryKey = $this->getStructure()->getPrimaryKey($joinTable);
$foreignKey = $this->getStructure()->getForeignKey($joinTable);
return " $clause $joinTable$asJoinAlias ON $joinAlias.$primaryKey = $mainTable.$foreignKey";
}
}
/**
* @return string
*/
protected function buildQuery() {
// first create extra join from statements with columns with referenced tables
$statementsWithReferences = array('WHERE', 'SELECT', 'GROUP BY', 'ORDER BY');
foreach ($statementsWithReferences as $clause) {
if (array_key_exists($clause, $this->statements)) {
$this->statements[$clause] = array_map(array($this, 'createUndefinedJoins'), $this->statements[$clause]);
}
}
return parent::buildQuery();
}
/**
* Create undefined joins from statement with column with referenced tables
*
* @param string $statement
*
* @return string - the rewritten $statement (e.g. tab1.tab2:col => tab2.col)
*/
private function createUndefinedJoins($statement) {
if (!$this->isSmartJoinEnabled) {
return $statement;
}
// matches a table name made of any printable characters followed by a dot/colon,
// followed by any letters, numbers and most punctuation (to exclude '*')
preg_match_all('/([^[:space:]\(\)]+[.:])[\p{L}\p{N}\p{Pd}\p{Pi}\p{Pf}\p{Pc}]*/u', $statement, $matches);
foreach ($matches[1] as $join) {
if (!in_array(substr($join, 0, -1), $this->joins)) {
$this->addJoinStatements('LEFT JOIN', $join);
}
}
// don't rewrite table from other databases
foreach ($this->joins as $join) {
if (strpos($join, '.') !== false && strpos($statement, $join) === 0) {
return $statement;
}
}
// remove extra referenced tables (rewrite tab1.tab2:col => tab2.col)
$statement = preg_replace('/(?:[^\s]*[.:])?([^\s]+)[.:]([^\s]*)/u', '$1.$2', $statement);
return $statement;
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace Envms\FluentPDO\Queries;
use Envms\FluentPDO\Query;
/**
* DELETE query builder
*
* @method Delete leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method Delete innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method Delete from(string $table) add LIMIT to query
* @method Delete orderBy(string $column) add ORDER BY to query
* @method Delete limit(int $limit) add LIMIT to query
*/
class Delete extends Common
{
private $ignore = false;
/**
* Delete constructor
*
* @param Query $fluent
* @param string $table
*/
public function __construct(Query $fluent, $table) {
$clauses = array(
'DELETE FROM' => array($this, 'getClauseDeleteFrom'),
'DELETE' => array($this, 'getClauseDelete'),
'FROM' => null,
'JOIN' => array($this, 'getClauseJoin'),
'WHERE' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
);
parent::__construct($fluent, $clauses);
$this->statements['DELETE FROM'] = $table;
$this->statements['DELETE'] = $table;
}
/**
* Forces delete operation to fail silently
*
* @return Delete
*/
public function ignore() {
$this->ignore = true;
return $this;
}
/**
* @return string
*/
protected function buildQuery() {
if ($this->statements['FROM']) {
unset($this->clauses['DELETE FROM']);
} else {
unset($this->clauses['DELETE']);
}
return parent::buildQuery();
}
/**
* Execute DELETE query
*
* @return bool
*/
public function execute() {
$result = parent::execute();
if ($result) {
return $result->rowCount();
}
return false;
}
/**
* @return string
*/
protected function getClauseDelete() {
return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' ' . $this->statements['DELETE'];
}
/**
* @return string
*/
protected function getClauseDeleteFrom() {
return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' FROM ' . $this->statements['DELETE FROM'];
}
}

View File

@ -0,0 +1,224 @@
<?php
namespace Envms\FluentPDO\Queries;
use Envms\FluentPDO\{Query,Literal};
/** INSERT query builder
*/
class Insert extends Base
{
/** @var array */
private $columns = array();
/** @var array */
private $firstValue = array();
/** @var bool */
private $ignore = false;
/** @var bool */
private $delayed = false;
/**
* InsertQuery constructor.
*
* @param Query $fluent
* @param string $table
* @param $values
*/
public function __construct(Query $fluent, $table, $values) {
$clauses = array(
'INSERT INTO' => array($this, 'getClauseInsertInto'),
'VALUES' => array($this, 'getClauseValues'),
'ON DUPLICATE KEY UPDATE' => array($this, 'getClauseOnDuplicateKeyUpdate'),
);
parent::__construct($fluent, $clauses);
$this->statements['INSERT INTO'] = $table;
$this->values($values);
}
/**
* Execute insert query
*
* @param mixed $sequence
*
* @return integer last inserted id or false
*/
public function execute($sequence = null) {
$result = parent::execute();
if ($result) {
return $this->getPDO()->lastInsertId($sequence);
}
return false;
}
/**
* Add ON DUPLICATE KEY UPDATE
*
* @param array $values
*
* @return Insert
*/
public function onDuplicateKeyUpdate($values) {
$this->statements['ON DUPLICATE KEY UPDATE'] = array_merge(
$this->statements['ON DUPLICATE KEY UPDATE'], $values
);
return $this;
}
/**
* Add VALUES
*
* @param $values
*
* @return Insert
* @throws \Exception
*/
public function values($values) {
if (!is_array($values)) {
throw new \Exception('Param VALUES for INSERT query must be array');
}
$first = current($values);
if (is_string(key($values))) {
// is one row array
$this->addOneValue($values);
} elseif (is_array($first) && is_string(key($first))) {
// this is multi values
foreach ($values as $oneValue) {
$this->addOneValue($oneValue);
}
}
return $this;
}
/**
* Force insert operation to fail silently
*
* @return Insert
*/
public function ignore() {
$this->ignore = true;
return $this;
}
/** Force insert operation delay support
*
* @return Insert
*/
public function delayed() {
$this->delayed = true;
return $this;
}
/**
* @return string
*/
protected function getClauseInsertInto() {
return 'INSERT' . ($this->ignore ? " IGNORE" : '') . ($this->delayed ? " DELAYED" : '') . ' INTO ' . $this->statements['INSERT INTO'];
}
/**
* @param $param
*
* @return string
*/
protected function parameterGetValue($param) {
return $param instanceof Literal ? (string)$param : '?';
}
/**
* @return string
*/
protected function getClauseValues() {
$valuesArray = array();
foreach ($this->statements['VALUES'] as $rows) {
// literals should not be parametrized.
// They are commonly used to call engine functions or literals.
// Eg: NOW(), CURRENT_TIMESTAMP etc
$placeholders = array_map(array($this, 'parameterGetValue'), $rows);
$valuesArray[] = '(' . implode(', ', $placeholders) . ')';
}
$columns = implode(', ', $this->columns);
$values = implode(', ', $valuesArray);
return " ($columns) VALUES $values";
}
/**
* Removes all Literal instances from the argument
* since they are not to be used as PDO parameters but rather injected directly into the query
*
* @param $statements
*
* @return array
*/
protected function filterLiterals($statements) {
$f = function ($item) {
return !$item instanceof Literal;
};
return array_map(function ($item) use ($f) {
if (is_array($item)) {
return array_filter($item, $f);
}
return $item;
}, array_filter($statements, $f));
}
/**
* @return array
*/
protected function buildParameters() {
$this->parameters = array_merge(
$this->filterLiterals($this->statements['VALUES']),
$this->filterLiterals($this->statements['ON DUPLICATE KEY UPDATE'])
);
return parent::buildParameters();
}
/**
* @return string
*/
protected function getClauseOnDuplicateKeyUpdate() {
$result = array();
foreach ($this->statements['ON DUPLICATE KEY UPDATE'] as $key => $value) {
$result[] = "$key = " . $this->parameterGetValue($value);
}
return ' ON DUPLICATE KEY UPDATE ' . implode(', ', $result);
}
/**
* @param array $oneValue
*
* @throws \Exception
*/
private function addOneValue($oneValue) {
// check if all $keys are strings
foreach ($oneValue as $key => $value) {
if (!is_string($key)) {
throw new \Exception('INSERT query: All keys of value array have to be strings.');
}
}
if (!$this->firstValue) {
$this->firstValue = $oneValue;
}
if (!$this->columns) {
$this->columns = array_keys($oneValue);
}
if ($this->columns != array_keys($oneValue)) {
throw new \Exception('INSERT query: All VALUES have to same keys (columns).');
}
$this->statements['VALUES'][] = $oneValue;
}
}

View File

@ -0,0 +1,201 @@
<?php
namespace Envms\FluentPDO\Queries;
use Envms\FluentPDO\{Query,Utilities};
/**
* SELECT query builder
*
* @method Select select(string $column) add one or more columns in SELECT to query
* @method Select leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method Select innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method Select groupBy(string $column) add GROUP BY to query
* @method Select having(string $column) add HAVING query
* @method Select orderBy(string $column) add ORDER BY to query
* @method Select limit(int $limit) add LIMIT to query
* @method Select offset(int $offset) add OFFSET to query
*/
class Select extends Common implements \Countable
{
/** @var mixed */
private $fromTable;
/** @var mixed */
private $fromAlias;
/** @var boolean */
private $convertTypes = false;
/**
* SelectQuery constructor.
*
* @param Query $fluent
* @param $from
*/
function __construct(Query $fluent, $from) {
$clauses = array(
'SELECT' => ', ',
'FROM' => null,
'JOIN' => array($this, 'getClauseJoin'),
'WHERE' => ' AND ',
'GROUP BY' => ',',
'HAVING' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
'OFFSET' => null,
"\n--" => "\n--",
);
parent::__construct($fluent, $clauses);
// initialize statements
$fromParts = explode(' ', $from);
$this->fromTable = reset($fromParts);
$this->fromAlias = end($fromParts);
$this->statements['FROM'] = $from;
$this->statements['SELECT'][] = $this->fromAlias . '.*';
$this->joins[] = $this->fromAlias;
if(isset($fluent->convertTypes) && $fluent->convertTypes){
$this->convertTypes = true;
}
}
/** Return table name from FROM clause
*
* @internal
*/
public function getFromTable() {
return $this->fromTable;
}
/** Return table alias from FROM clause
*
* @internal
*/
public function getFromAlias() {
return $this->fromAlias;
}
/**
* Returns a single column
*
* @param int $columnNumber
*
* @return string
*/
public function fetchColumn($columnNumber = 0) {
if (($s = $this->execute()) !== false) {
return $s->fetchColumn($columnNumber);
}
return $s;
}
/**
* Fetch first row or column
*
* @param string $column column name or empty string for the whole row
*
* @return mixed string, array or false if there is no row
*/
public function fetch($column = '') {
$s = $this->execute();
if ($s === false) {
return false;
}
$row = $s->fetch();
if($this->convertTypes){
$row = Utilities::convertToNativeTypes($s,$row);
}
if ($row && $column != '') {
if (is_object($row)) {
return $row->{$column};
} else {
return $row[$column];
}
}
return $row;
}
/**
* Fetch pairs
*
* @param $key
* @param $value
* @param $object
*
* @return array of fetched rows as pairs
*/
public function fetchPairs($key, $value, $object = false) {
if (($s = $this->select(null)->select("$key, $value")->asObject($object)->execute()) !== false) {
return $s->fetchAll(\PDO::FETCH_KEY_PAIR);
}
return $s;
}
/** Fetch all row
*
* @param string $index specify index column
* @param string $selectOnly select columns which could be fetched
*
* @return \PDOStatement|array of fetched rows
*/
public function fetchAll($index = '', $selectOnly = '') {
if ($selectOnly) {
$this->select(null)->select($index . ', ' . $selectOnly);
}
if ($index) {
$data = [];
foreach ($this as $row) {
if (is_object($row)) {
$data[$row->{$index}] = $row;
} else {
$data[$row[$index]] = $row;
}
}
return $data;
} else {
if (($s = $this->execute()) !== false) {
if($this->convertTypes){
return Utilities::convertToNativeTypes($s, $s->fetchAll());
} else {
return $s->fetchAll();
}
}
return $s;
}
}
/**
* \Countable interface doesn't break current select query
*
* @return int
*/
public function count() {
$fluent = clone $this;
return (int)$fluent->select(null)->select('COUNT(*)')->fetchColumn();
}
/**
* @return \ArrayIterator|\PDOStatement
* @todo look into \Countable implementation
*/
public function getIterator() {
if ($this->convertTypes) {
return new \ArrayIterator($this->fetchAll());
} else {
return $this->execute();
}
}
}

View File

@ -0,0 +1,111 @@
<?php
namespace Envms\FluentPDO\Queries;
use Envms\FluentPDO\{Query,Literal};
/**
* UPDATE query builder
*
* @method Update leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method Update innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method Update orderBy(string $column) add ORDER BY to query
* @method Update limit(int $limit) add LIMIT to query
*/
class Update extends Common
{
/**
* UpdateQuery constructor
*
* @param Query $fluent
* @param $table
*/
public function __construct(Query $fluent, $table) {
$clauses = array(
'UPDATE' => array($this, 'getClauseUpdate'),
'JOIN' => array($this, 'getClauseJoin'),
'SET' => array($this, 'getClauseSet'),
'WHERE' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
);
parent::__construct($fluent, $clauses);
$this->statements['UPDATE'] = $table;
$tableParts = explode(' ', $table);
$this->joins[] = end($tableParts);
}
/**
* @param string|array $fieldOrArray
* @param bool|string $value
*
* @return $this
* @throws \Exception
*/
public function set($fieldOrArray, $value = false) {
if (!$fieldOrArray) {
return $this;
}
if (is_string($fieldOrArray) && $value !== false) {
$this->statements['SET'][$fieldOrArray] = $value;
} else {
if (!is_array($fieldOrArray)) {
throw new \Exception('You must pass a value, or provide the SET list as an associative array. column => value');
} else {
foreach ($fieldOrArray as $field => $value) {
$this->statements['SET'][$field] = $value;
}
}
}
return $this;
}
/**
* Execute update query
*
* @param boolean $getResultAsPdoStatement true to return the pdo statement instead of row count
*
* @return int|boolean|\PDOStatement
*/
public function execute($getResultAsPdoStatement = false) {
$result = parent::execute();
if ($getResultAsPdoStatement) {
return $result;
}
if ($result) {
return $result->rowCount();
}
return false;
}
/**
* @return string
*/
protected function getClauseUpdate() {
return 'UPDATE ' . $this->statements['UPDATE'];
}
/**
* @return string
*/
protected function getClauseSet() {
$setArray = array();
foreach ($this->statements['SET'] as $field => $value) {
if ($value instanceof Literal) {
$setArray[] = $field . ' = ' . $value;
} else {
$setArray[] = $field . ' = ?';
$this->parameters['SET'][$field] = $value;
}
}
return ' SET ' . implode(', ', $setArray);
}
}

View File

@ -0,0 +1,158 @@
<?php
namespace Envms\FluentPDO;
use Envms\FluentPDO\Queries\{Insert,Select,Update,Delete};
/**
* FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.
*
* For more information see readme.md
*
* @link http://github.com/envms/fluentpdo
* @author envms, start@env.ms
* @copyright 2012-2017 env.ms - Chris Bornhoft, Aldo Matelli, Stefan Yohansson, Kevin Sanabria, Carol Zhang, Marek Lichtner
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
/**
* Class Query
*/
class Query
{
/** @var \PDO */
protected $pdo;
/** @var Structure|null */
protected $structure;
/** @var bool|callback */
public $debug;
/** @var boolean */
public $convertTypes = false;
/**
* Query constructor
*
* @param \PDO $pdo
* @param Structure|null $structure
*/
function __construct(\PDO $pdo, Structure $structure = null) {
$this->pdo = $pdo;
if (!$structure) {
$structure = new Structure();
}
$this->structure = $structure;
}
/**
* Create SELECT query from $table
*
* @param string $table - db table name
* @param integer $primaryKey - return one row by primary key
*
* @return Select
*/
public function from($table, $primaryKey = null) {
$query = new Select($this, $table);
if ($primaryKey !== null) {
$tableTable = $query->getFromTable();
$tableAlias = $query->getFromAlias();
$primaryKeyName = $this->structure->getPrimaryKey($tableTable);
$query = $query->where("$tableAlias.$primaryKeyName", $primaryKey);
}
return $query;
}
/**
* Create INSERT INTO query
*
* @param string $table
* @param array $values - accepts one or multiple rows, @see docs
*
* @return Insert
*/
public function insertInto($table, $values = array()) {
$query = new Insert($this, $table, $values);
return $query;
}
/**
* Create UPDATE query
*
* @param string $table
* @param array|string $set
* @param string $primaryKey
*
* @return Update
*/
public function update($table, $set = array(), $primaryKey = null) {
$query = new Update($this, $table);
$query->set($set);
if ($primaryKey) {
$primaryKeyName = $this->getStructure()->getPrimaryKey($table);
$query = $query->where($primaryKeyName, $primaryKey);
}
return $query;
}
/**
* Create DELETE query
*
* @param string $table
* @param string $primaryKey delete only row by primary key
*
* @return Delete
*/
public function delete($table, $primaryKey = null) {
$query = new Delete($this, $table);
if ($primaryKey) {
$primaryKeyName = $this->getStructure()->getPrimaryKey($table);
$query = $query->where($primaryKeyName, $primaryKey);
}
return $query;
}
/**
* Create DELETE FROM query
*
* @param string $table
* @param string $primaryKey
*
* @return Delete
*/
public function deleteFrom($table, $primaryKey = null) {
$args = func_get_args();
return call_user_func_array(array($this, 'delete'), $args);
}
/**
* @return \PDO
*/
public function getPdo() {
return $this->pdo;
}
/**
* @return Structure
*/
public function getStructure() {
return $this->structure;
}
/**
* Closes the \PDO connection to the database
*
* @return null
*/
public function close() {
$this->pdo = null;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Envms\FluentPDO;
/**
* Class Structure
*/
class Structure
{
/** @var string */
private $primaryKey;
/** @var string */
private $foreignKey;
/**
* Structure constructor
*
* @param string $primaryKey
* @param string $foreignKey
*/
function __construct($primaryKey = 'id', $foreignKey = '%s_id') {
if ($foreignKey === null) {
$foreignKey = $primaryKey;
}
$this->primaryKey = $primaryKey;
$this->foreignKey = $foreignKey;
}
/**
* @param string $table
*
* @return string
*/
public function getPrimaryKey($table) {
return $this->key($this->primaryKey, $table);
}
/**
* @param string $table
*
* @return string
*/
public function getForeignKey($table) {
return $this->key($this->foreignKey, $table);
}
/**
* @param string|callback $key
* @param string $table
*
* @return string
*/
private function key($key, $table) {
if (is_callable($key)) {
return $key($table);
}
return sprintf($key, $table);
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace Envms\FluentPDO;
/**
* Class Utilities
*/
class Utilities
{
/**
* Convert "camelCaseWord" to "CAMEL CASE WORD"
*
* @param string $string
*
* @return string
*/
public static function toUpperWords($string) {
return trim(strtoupper(preg_replace('/(.)([A-Z]+)/', '$1 $2', $string)));
}
/**
* @param string $query
*
* @return string
*/
public static function formatQuery($query) {
$query = preg_replace(
'/\b(WHERE|FROM|GROUP BY|HAVING|ORDER BY|LIMIT|OFFSET|UNION|ON DUPLICATE KEY UPDATE|VALUES|SET)\b/',
"\n$0", $query
);
$query = preg_replace(
'/\b(INNER|OUTER|LEFT|RIGHT|FULL|CASE|WHEN|END|ELSE|AND)\b/',
"\n $0", $query
);
$query = preg_replace("/\s+\n/", "\n", $query); // remove trailing spaces
return $query;
}
/**
* Converts columns from strings to types according to
* PDOStatement::columnMeta
* http://stackoverflow.com/a/9952703/3006989
*
* @param \PDOStatement $statement
* @param array|\Traversable $rows - provided by PDOStatement::fetch with PDO::FETCH_ASSOC
* @return array|\Traversable - copy of $assoc with matching type fields
*/
public static function convertToNativeTypes(\PDOStatement $statement, $rows)
{
for ($i = 0; ($columnMeta = $statement->getColumnMeta($i)) !== false; $i++)
{
$type = $columnMeta['native_type'];
switch($type)
{
case 'DECIMAL':
case 'NEWDECIMAL':
case 'FLOAT':
case 'DOUBLE':
case 'TINY':
case 'SHORT':
case 'LONG':
case 'LONGLONG':
case 'INT24':
if(isset($rows[$columnMeta['name']])){
$rows[$columnMeta['name']] = $rows[$columnMeta['name']] + 0;
}else{
if(is_array($rows) || $rows instanceof \Traversable){
foreach($rows as &$row){
if(isset($row[$columnMeta['name']])){
$row[$columnMeta['name']] = $row[$columnMeta['name']] + 0;
}
}
}
}
break;
case 'DATETIME':
case 'DATE':
case 'TIMESTAMP':
// convert to date type?
break;
// default: keep as string
}
}
return $rows;
}
/**
* @param $subject
*
* @return bool
*/
public static function isCountable($subject) {
return (is_array($subject) || ($subject instanceof \Countable));
}
}

View File

@ -0,0 +1,31 @@
--TEST--
Basic operations
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->where('id > ?', 0)->orderBy('name');
$query = $query->where('name = ?', 'Marek');
echo $query->getQuery() . "\n";
print_r($query->getParameters());
print_r($query->fetch());
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE id > ?
AND name = ?
ORDER BY name
Array
(
[0] => 0
[1] => Marek
)
Array
(
[id] => 1
[country_id] => 1
[type] => admin
[name] => Marek
)

View File

@ -0,0 +1,25 @@
--TEST--
Query with select, group, having, order
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent
->from('user')
->select(null)
->select('type, count(id) AS type_count')
->where('id > ?', 1)
->groupBy('type')
->having('type_count > ?', 1)
->orderBy('name');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT type, count(id) AS type_count
FROM user
WHERE id > ?
GROUP BY type
HAVING type_count > ?
ORDER BY name

View File

@ -0,0 +1,20 @@
--TEST--
from($table, $id)
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user', 2);
echo $query->getQuery() . "\n";
print_r($query->getParameters());
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE user.id = ?
Array
(
[0] => 2
)

View File

@ -0,0 +1,25 @@
--TEST--
where(array(...))
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->where(array(
'id' => 2,
'type' => 'author',
));
echo $query->getQuery() . "\n";
print_r($query->getParameters());
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE id = ?
AND type = ?
Array
(
[0] => 2
[1] => author
)

View File

@ -0,0 +1,20 @@
--TEST--
where('column', 'value')
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->where('type', 'author');
echo $query->getQuery() . "\n";
print_r($query->getParameters());
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE type = ?
Array
(
[0] => author
)

View File

@ -0,0 +1,15 @@
--TEST--
where('column', null)
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->where('type', null);
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE type is NULL

View File

@ -0,0 +1,19 @@
--TEST--
where('column', array(..))
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->where('id', array(1,2,3));
echo $query->getQuery() . "\n";
print_r($query->getParameters());
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE id IN (1, 2, 3)
Array
(
)

View File

@ -0,0 +1,30 @@
--TEST--
where with named :params
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')
->where('type = :type', array(':type' => 'author'))
->where('id > :id AND name <> :name', array(':id' => 1, ':name' => 'Marek'));
echo $query->getQuery() . "\n";
print_r($query->getParameters());
foreach ($query as $row) {
echo "$row[name]\n";
}
?>
--EXPECTF--
SELECT user.*
FROM user
WHERE type = :type
AND id > :id
AND name <> :name
Array
(
[:type] => author
[:id] => 1
[:name] => Marek
)
Robert

View File

@ -0,0 +1,25 @@
--TEST--
full join
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')
->select('user.name')
->leftJoin('user ON user.id = article.user_id')
->orderBy('article.title');
echo $query->getQuery() . "\n";
foreach ($query as $row) {
echo "$row[name] - $row[title]\n";
}
?>
--EXPECTF--
SELECT article.*, user.name
FROM article
LEFT JOIN user ON user.id = article.user_id
ORDER BY article.title
Marek - article 1
Robert - article 2
Marek - article 3

View File

@ -0,0 +1,25 @@
--TEST--
short join - default join is left join
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->leftJoin('user');
echo $query->getQuery() . "\n";
$query = $fluent->from('article')->leftJoin('user author');
echo $query->getQuery() . "\n";
$query = $fluent->from('article')->leftJoin('user AS author');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*
FROM article
LEFT JOIN user ON user.id = article.user_id
SELECT article.*
FROM article
LEFT JOIN user AS author ON author.id = article.user_id
SELECT article.*
FROM article
LEFT JOIN user AS author ON author.id = article.user_id

View File

@ -0,0 +1,24 @@
--TEST--
short join back reference
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->innerJoin('article:');
echo $query->getQuery() . "\n";
$query = $fluent->from('user')->innerJoin('article: with_articles');
echo $query->getQuery() . "\n";
$query = $fluent->from('user')->innerJoin('article: AS with_articles');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT user.*
FROM user
INNER JOIN article ON article.user_id = user.id
SELECT user.*
FROM user
INNER JOIN article AS with_articles ON with_articles.user_id = user.id
SELECT user.*
FROM user
INNER JOIN article AS with_articles ON with_articles.user_id = user.id

View File

@ -0,0 +1,15 @@
--TEST--
join same two tables
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('comment')->leftJoin('article.user');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT comment.*
FROM comment
LEFT JOIN article ON article.id = comment.article_id
LEFT JOIN user ON user.id = article.user_id

View File

@ -0,0 +1,24 @@
--TEST--
multi short join
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->innerJoin('comment:user AS comment_user');
echo $query->getQuery() . "\n";
print_r($query->fetch());
?>
--EXPECTF--
SELECT article.*
FROM article
INNER JOIN comment ON comment.article_id = article.id
INNER JOIN user AS comment_user ON comment_user.id = comment.user_id
Array
(
[id] => 1
[user_id] => 1
[published_at] => 2011-12-10 12:10:00
[title] => article 1
[content] => content 1
)

View File

@ -0,0 +1,15 @@
--TEST--
join two same tables
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->leftJoin('user')->leftJoin('user');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*
FROM article
LEFT JOIN user ON user.id = article.user_id

View File

@ -0,0 +1,32 @@
--TEST--
join two tables via difference keys
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('comment')
->where('comment.id', 1)
->leftJoin('user comment_author')->select('comment_author.name AS comment_name')
->leftJoin('article.user AS article_author')->select('article_author.name AS author_name');
echo $query->getQuery() . "\n";
$result = $query->fetch();
print_r($result);
?>
--EXPECTF--
SELECT comment.*, comment_author.name AS comment_name, article_author.name AS author_name
FROM comment
LEFT JOIN user AS comment_author ON comment_author.id = comment.user_id
LEFT JOIN article ON article.id = comment.article_id
LEFT JOIN user AS article_author ON article_author.id = article.user_id
WHERE comment.id = ?
Array
(
[id] => 1
[article_id] => 1
[user_id] => 2
[content] => comment 1.1
[comment_name] => Robert
[author_name] => Marek
)

View File

@ -0,0 +1,21 @@
--TEST--
Basic operations
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
echo "'" . Envms\FluentPDO\Utilities::toUpperWords('one') . "'\n";
echo "'" . Envms\FluentPDO\Utilities::toUpperWords(' one ') . "'\n";
echo "'" . Envms\FluentPDO\Utilities::toUpperWords('oneTwo') . "'\n";
echo "'" . Envms\FluentPDO\Utilities::toUpperWords('OneTwo') . "'\n";
echo "'" . Envms\FluentPDO\Utilities::toUpperWords('oneTwoThree') . "'\n";
echo "'" . Envms\FluentPDO\Utilities::toUpperWords(' oneTwoThree ') . "'\n";
?>
--EXPECTF--
'ONE'
'ONE'
'ONE TWO'
'ONE TWO'
'ONE TWO THREE'
'ONE TWO THREE'

View File

@ -0,0 +1,19 @@
--TEST--
join in where
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->where('comment:content <> "" AND user.country.id = ?', 1);
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*
FROM article
LEFT JOIN comment ON comment.article_id = article.id
LEFT JOIN user ON user.id = article.user_id
LEFT JOIN country ON country.id = user.country_id
WHERE comment.content <> ""
AND country.id = ?

View File

@ -0,0 +1,15 @@
--TEST--
join in where
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->select('user.name as author');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*, user.name as author
FROM article
LEFT JOIN user ON user.id = article.user_id

View File

@ -0,0 +1,16 @@
--TEST--
join in where
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->orderBy('user.name, article.title');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*
FROM article
LEFT JOIN user ON user.id = article.user_id
ORDER BY user.name, article.title

View File

@ -0,0 +1,33 @@
--TEST--
join in where
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->groupBy('user.type')
->select(null)->select('user.type, count(article.id) as article_count');
echo $query->getQuery() . "\n";
$result = $query->fetchAll();
print_r($result);
?>
--EXPECTF--
SELECT user.type, count(article.id) as article_count
FROM article
LEFT JOIN user ON user.id = article.user_id
GROUP BY user.type
Array
(
[0] => Array
(
[type] => admin
[article_count] => 2
)
[1] => Array
(
[type] => author
[article_count] => 1
)
)

View File

@ -0,0 +1,35 @@
--TEST--
don't create second join if table or alias was joined
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->innerJoin('user AS author ON article.user_id = author.id')
->select('author.name');
echo $query->getQuery() . "\n";
$query = $fluent->from('article')->innerJoin('user ON article.user_id = user.id')
->select('user.name');
echo $query->getQuery() . "\n";
$query = $fluent->from('article')->innerJoin('user AS author ON article.user_id = author.id')
->select('author.country.name');
echo $query->getQuery() . "\n";
$query = $fluent->from('article')->innerJoin('user ON article.user_id = user.id')
->select('user.country.name');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*, author.name
FROM article
INNER JOIN user AS author ON article.user_id = author.id
SELECT article.*, user.name
FROM article
INNER JOIN user ON article.user_id = user.id
SELECT article.*, country.name
FROM article
INNER JOIN user AS author ON article.user_id = author.id
LEFT JOIN country ON country.id = author.country_id
SELECT article.*, country.name
FROM article
INNER JOIN user ON article.user_id = user.id
LEFT JOIN country ON country.id = user.country_id

View File

@ -0,0 +1,24 @@
--TEST--
clause with referenced table before join
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->select('user.name')->innerJoin('user');
echo $query->getQuery() . "\n";
$query = $fluent->from('article')->select('author.name')->innerJoin('user as author');
echo $query->getQuery() . "\n";
$query = $fluent->from('user')->select('article:title')->innerJoin('article:');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*, user.name
FROM article
INNER JOIN user ON user.id = article.user_id
SELECT article.*, author.name
FROM article
INNER JOIN user AS author ON author.id = article.user_id
SELECT user.*, article.title
FROM user
INNER JOIN article ON article.user_id = user.id

View File

@ -0,0 +1,15 @@
--TEST--
aliases for clauses: group -> groupBy, order -> orderBy
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('article')->group('user_id')->order('id');
echo $query->getQuery() . "\n";
?>
--EXPECTF--
SELECT article.*
FROM article
GROUP BY user_id
ORDER BY id

View File

@ -0,0 +1,24 @@
--TEST--
fetch
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
echo $fluent->from('user', 1)->fetch('name') . "\n";
print_r($fluent->from('user', 1)->fetch());
if ($fluent->from('user', 3)->fetch() === false) echo "false\n";
if ($fluent->from('user', 3)->fetch('name') === false) echo "false\n";
?>
--EXPECTF--
Marek
Array
(
[id] => 1
[country_id] => 1
[type] => admin
[name] => Marek
)
false
false

View File

@ -0,0 +1,38 @@
--TEST--
fetch pairs, fetch all
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$result = $fluent->from('user')->fetchPairs('id', 'name');
print_r($result);
$result = $fluent->from('user')->fetchAll();
print_r($result);
?>
--EXPECTF--
Array
(
[1] => Marek
[2] => Robert
)
Array
(
[0] => Array
(
[id] => 1
[country_id] => 1
[type] => admin
[name] => Marek
)
[1] => Array
(
[id] => 2
[country_id] => 1
[type] => author
[name] => Robert
)
)

View File

@ -0,0 +1,27 @@
--TEST--
debug callback
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
/**
* $fluent->debug = true; // log queries to STDERR
* $fluent->debug = $callback; // see below
*/
$fluent->debug = function($BaseQuery) {
echo "query: " . $BaseQuery->getQuery(false) . "\n";
echo "parameters: " . implode(', ', $BaseQuery->getParameters()) . "\n";
echo "rowCount: " . $BaseQuery->getResult()->rowCount() . "\n";
// time is impossible to test (each time is other)
// echo $FluentQuery->getTime() . "\n";
};
$fluent->from('user')->where('id < ? AND name <> ?', 7, 'Peter')->execute();
$fluent->debug = null;
?>
--EXPECTF--
query: SELECT user.* FROM user WHERE id < ? AND name <> ?
parameters: 7, Peter
rowCount: 2

View File

@ -0,0 +1,29 @@
--TEST--
fetch all with params
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$result = $fluent->from('user')->fetchAll('id', 'type, name');
print_r($result);
?>
--EXPECTF--
Array
(
[1] => Array
(
[id] => 1
[type] => admin
[name] => Marek
)
[2] => Array
(
[id] => 2
[type] => author
[name] => Robert
)
)

View File

@ -0,0 +1,15 @@
--TEST--
FROM table from other database
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('db2.user')->order('db2.user.name')->getQuery();
echo "$query\n";
?>
--EXPECTF--
SELECT db2.user.*
FROM db2.user
ORDER BY db2.user.name

View File

@ -0,0 +1,18 @@
--TEST--
FROM table from other database
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')
->innerJoin('db2.types ON db2.types.id = user.type')
->select('db2.types.*')
->getQuery();
echo "$query\n";
?>
--EXPECTF--
SELECT user.*, db2.types.*
FROM user
INNER JOIN db2.types ON db2.types.id = user.type

View File

@ -0,0 +1,41 @@
--TEST--
join using USING
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var $fluent2 Envms\FluentPDO\Query */
$fluent_structure2 = new Envms\FluentPDO\Structure('%s_id', '%s_id');
$fluent2 = new Envms\FluentPDO\Query($pdo, $fluent_structure2);
$query = $fluent2->from('article')
->innerJoin('user USING (user_id)')
->select('user.*')
->getQuery();
echo "$query\n";
$query = $fluent2->from('article')
->innerJoin('user u USING (user_id)')
->select('u.*')
->getQuery();
echo "$query\n";
$query = $fluent2->from('article')
->innerJoin('user AS u USING (user_id)')
->select('u.*')
->getQuery();
echo "$query\n";
unset($fluent_structure2);
unset($fluent2);
?>
--EXPECTF--
SELECT article.*, user.*
FROM article
INNER JOIN user USING (user_id)
SELECT article.*, u.*
FROM article
INNER JOIN user u USING (user_id)
SELECT article.*, u.*
FROM article
INNER JOIN user AS u USING (user_id)

View File

@ -0,0 +1,28 @@
--TEST--
FROM with alias
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user author')->getQuery();
echo "$query\n";
$query = $fluent->from('user AS author')->getQuery();
echo "$query\n";
$query = $fluent->from('user AS author', 1)->getQuery();
echo "$query\n";
$query = $fluent->from('user AS author')->select('country.name')->getQuery();
echo "$query\n";
?>
--EXPECTF--
SELECT author.*
FROM user author
SELECT author.*
FROM user AS author
SELECT author.*
FROM user AS author
WHERE author.id = ?
SELECT author.*, country.name
FROM user AS author
LEFT JOIN country ON country.id = user AS author.country_id

View File

@ -0,0 +1,29 @@
--TEST--
insert into
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->insertInto('article',
array(
'user_id' => 1,
'title' => 'new title',
'content' => 'new content'
));
echo $query->getQuery() . "\n";
print_r($query->getParameters());
$lastInsert = $query->execute();
$pdo->query('DELETE FROM article WHERE id > 3')->execute();
?>
--EXPECTF--
INSERT INTO article (user_id, title, content)
VALUES (?, ?, ?)
Array
(
[0] => 1
[1] => new title
[2] => new content
)

View File

@ -0,0 +1,54 @@
--TEST--
INSERT with ON DUPLICATE KEY UPDATE
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/** @var Envms\FluentPDO\Query */
$query = $fluent->insertInto('article', array('id' => 1))
->onDuplicateKeyUpdate(array(
'title' => 'article 1b',
'content' => new Envms\FluentPDO\Literal('abs(-1)') // let's update with a literal and a parameter value
));
echo $query->getQuery() . "\n";
print_r($query->getParameters());
echo 'last_inserted_id = ' . $query->execute() . "\n";
$q = $fluent->from('article', 1)->fetch();
print_r($q);
$query = $fluent->insertInto('article', array('id' => 1))
->onDuplicateKeyUpdate(array(
'title' => 'article 1',
'content' => 'content 1',
))->execute();
echo "last_inserted_id = $query\n";
$q = $fluent->from('article', 1)->fetch();
print_r($q);
?>
--EXPECTF--
INSERT INTO article (id)
VALUES (?)
ON DUPLICATE KEY UPDATE title = ?, content = abs(-1)
Array
(
[0] => 1
[1] => article 1b
)
last_inserted_id = 1
Array
(
[id] => 1
[user_id] => 1
[published_at] => 2011-12-10 12:10:00
[title] => article 1b
[content] => 1
)
last_inserted_id = 1
Array
(
[id] => 1
[user_id] => 1
[published_at] => 2011-12-10 12:10:00
[title] => article 1
[content] => content 1
)

View File

@ -0,0 +1,26 @@
--TEST--
insert ignore
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->insertInto('article',
array(
'user_id' => 1,
'title' => 'new title',
'content' => 'new content',
))->ignore();
echo $query->getQuery() . "\n";
print_r($query->getParameters());
?>
--EXPECTF--
INSERT IGNORE INTO article (user_id, title, content)
VALUES (?, ?, ?)
Array
(
[0] => 1
[1] => new title
[2] => new content
)

View File

@ -0,0 +1,28 @@
--TEST--
insert with literal
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->insertInto('article',
array(
'user_id' => 1,
'updated_at' => new Envms\FluentPDO\Literal('NOW()'),
'title' => 'new title',
'content' => 'new content',
));
echo $query->getQuery() . "\n";
print_r($query->getParameters());
?>
--EXPECTF--
INSERT INTO article (user_id, updated_at, title, content)
VALUES (?, NOW(), ?, ?)
Array
(
[0] => 1
[1] => new title
[2] => new content
)

View File

@ -0,0 +1,49 @@
--TEST--
Disable and enable smart join feature
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('comment')
->select('user.name')
->orderBy('article.published_at')
->getQuery();
echo "-- Plain:\n$query\n\n";
$query = $fluent->from('comment')
->select('user.name')
->disableSmartJoin()
->orderBy('article.published_at')
->getQuery();
echo "-- Disable:\n$query\n\n";
$query = $fluent->from('comment')
->disableSmartJoin()
->select('user.name')
->enableSmartJoin()
->orderBy('article.published_at')
->getQuery();
echo "-- Disable and enable:\n$query\n\n";
?>
--EXPECTF--
-- Plain:
SELECT comment.*, user.name
FROM comment
LEFT JOIN user ON user.id = comment.user_id
LEFT JOIN article ON article.id = comment.article_id
ORDER BY article.published_at
-- Disable:
SELECT comment.*, user.name
FROM comment
ORDER BY article.published_at
-- Disable and enable:
SELECT comment.*, user.name
FROM comment
LEFT JOIN user ON user.id = comment.user_id
LEFT JOIN article ON article.id = comment.article_id
ORDER BY article.published_at

View File

@ -0,0 +1,18 @@
--TEST--
fetch column
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
echo $fluent->from('user', 1)->fetchColumn() . "\n";
echo $fluent->from('user', 1)->fetchColumn(3) . "\n";
if ($fluent->from('user', 3)->fetchColumn() === false) echo "false\n";
if ($fluent->from('user', 3)->fetchColumn(3) === false) echo "false\n";
?>
--EXPECTF--
1
Marek
false
false

View File

@ -0,0 +1,31 @@
--TEST--
PDO::FETCH_OBJ option.
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->from('user')->where('id > ?', 0)->orderBy('name');
$query = $query->where('name = ?', 'Marek');
$fluent->getPdo()->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
print_r($query->getParameters());
print_r($query->fetch());
// Set back for other tests.
$fluent->getPdo()->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_BOTH);
?>
--EXPECTF--
Array
(
[0] => 0
[1] => Marek
)
stdClass Object
(
[id] => 1
[country_id] => 1
[type] => admin
[name] => Marek
)

View File

@ -0,0 +1,39 @@
--TEST--
Basic update
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->update('country')->set('name', 'aikavolS')->where('id', 1);
$query->execute();
echo $query->getQuery() . "\n";
print_r($query->getParameters()) . "\n";
$query = $fluent->from('country')->where('id', 1);
print_r($query->fetch());
$fluent->update('country')->set('name', 'Slovakia')->where('id', 1)->execute();
$query = $fluent->from('country')->where('id', 1);
print_r($query->fetch());
?>
--EXPECTF--
UPDATE country
SET name = ?
WHERE id = ?
Array
(
[0] => aikavolS
[1] => 1
)
Array
(
[id] => 1
[name] => aikavolS
)
Array
(
[id] => 1
[name] => Slovakia
)

View File

@ -0,0 +1,19 @@
--TEST--
Basic update
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->update('article')->set('published_at', new Envms\FluentPDO\Literal('NOW()'))->where('user_id', 1);
echo $query->getQuery() . "\n";
print_r($query->getParameters()) . "\n";
?>
--EXPECTF--
UPDATE article
SET published_at = NOW()
WHERE user_id = ?
Array
(
[0] => 1
)

View File

@ -0,0 +1,25 @@
--TEST--
Basic update
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->update('user')->set(array('name' => 'keraM', '`type`' => 'author'))->where('id', 1);
$query->execute();
echo $query->getQuery() . "\n";
print_r($query->getParameters()) . "\n";
$query = $fluent->update('user')->set(array('name' => 'Marek', '`type`' => 'admin'))->where('id', 1);
$query->execute();
?>
--EXPECTF--
UPDATE user
SET name = ?, `type` = ?
WHERE id = ?
Array
(
[0] => keraM
[1] => author
[2] => 1
)

View File

@ -0,0 +1,26 @@
--TEST--
Basic update
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->update('user')
->outerJoin('country ON country.id = user.country_id')
->set(array('name' => 'keraM', '`type`' => 'author'))
->where('id', 1);
echo $query->getQuery() . "\n";
print_r($query->getParameters()) . "\n";
?>
--EXPECTF--
UPDATE user
OUTER JOIN country ON country.id = user.country_id
SET name = ?, `type` = ?
WHERE id = ?
Array
(
[0] => keraM
[1] => author
[2] => 1
)

View File

@ -0,0 +1,24 @@
--TEST--
Update with smart join
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->update('user')
->set(array('type' => 'author'))
->where('country.id', 1);
echo $query->getQuery() . "\n";
print_r($query->getParameters()) . "\n";
?>
--EXPECTF--
UPDATE user
LEFT JOIN country ON country.id = user.country_id
SET type = ?
WHERE country.id = ?
Array
(
[0] => author
[1] => 1
)

View File

@ -0,0 +1,27 @@
--TEST--
Update with ORDER BY and LIMIT
--FILE--
<?php
include_once dirname(__FILE__) . "/connect.inc.php";
/* @var Envms\FluentPDO\Query */
$query = $fluent->update('user')
->set(array('type' => 'author'))
->where('id', 2)
->orderBy('name')
->limit(1);
echo $query->getQuery() . "\n";
print_r($query->getParameters()) . "\n";
?>
--EXPECTF--
UPDATE user
SET type = ?
WHERE id = ?
ORDER BY name
LIMIT 1
Array
(
[0] => author
[1] => 2
)

Some files were not shown because too many files have changed in this diff Show More