74 Commits

Author SHA1 Message Date
0f3febc00d 2022-12-20 2022-12-20 14:13:05 -03:00
85fef16b27 Path 2022-08-08 23:03:28 -04:00
cdb4b382b7 Limpieza de objetos externos 2022-08-08 22:36:04 -04:00
a9968dec58 Version 3.0
New technologies
2022-08-05 21:28:59 -04:00
06071884c7 FIX: Negative numbers in cuenta ui, and update_consolidar command route 2022-04-18 22:41:25 -04:00
6ff584013f Add update consolidado command and queuing 2022-04-18 22:13:17 -04:00
cf27465d75 Improve readability 2022-04-18 22:11:59 -04:00
560fb356fa Move to today in cuentas.show 2022-04-02 23:32:27 -04:00
10e5383a3e FIX: modalToAdd in cuentas.show 2022-04-02 23:58:57 -03:00
bc5338b1f1 FIX: consolidado not changed with tipocambio 2022-04-02 23:40:53 -03:00
4db8161c70 FIX: withJson requires 2nd parameter 2022-03-26 20:55:57 -03:00
d94f076946 Script structure and searchable cuentas 2022-03-25 20:54:02 -03:00
8009433958 FIX: update buttons 2022-03-25 17:52:21 -03:00
d7b406ef25 FIX: double transform value 2022-03-25 17:45:58 -03:00
cddf768b1e FIX: show cuentas missing moneda 2022-03-25 17:39:12 -03:00
8b86560973 Error catching in UI 2022-03-25 16:36:58 -03:00
4ea5c6de3e Env files 2022-03-25 16:21:38 -03:00
91cc28e681 Merge branch 'feature/commands' into develop 2022-03-25 16:18:38 -03:00
ef01ab3c55 Migrations 2022-03-25 16:18:09 -03:00
e41ffd505b Migrations fixed 2022-03-25 16:16:31 -03:00
2986806137 Merge branch 'feature/commands' into develop 2022-03-25 15:04:53 -03:00
f9076d3bac Error catching in API 2022-03-25 15:04:42 -03:00
ee3133da72 Consolidar command 2022-03-25 15:04:10 -03:00
dbad283e14 Command queue 2022-03-25 15:03:49 -03:00
e3737aba27 Consolidar 2022-03-25 10:11:02 -03:00
fcc84ac09c Console application 2022-03-25 10:10:43 -03:00
2b3b475d91 Show one month at a time, and change month with calendar and buttons 2022-03-18 17:50:21 -03:00
e5cf3cfa07 Update blade view 2022-03-18 17:43:26 -03:00
84a3f8e2e3 Order totals for tipocuenta 2022-01-07 01:12:54 -03:00
5a9dc6602c Bold results 2022-01-07 00:24:08 -03:00
d7dfc2d221 FIX:Unavailable value for tipocambio crashed the loading of values 2022-01-07 00:23:52 -03:00
c1eeba04a2 Resultado y fix cuentas 2022-01-06 15:20:21 -03:00
3cb2a877de FIX: transacciones, tipocambio and headers 2022-01-06 15:19:36 -03:00
b17225549c Python get cambio 2022-01-05 21:10:51 -03:00
af78106700 Cleaner code 2022-01-05 16:01:27 -03:00
665f426011 Sort cuentas in home 2022-01-05 15:58:03 -03:00
56b371d20c Refresh and edit transacciones 2022-01-05 15:56:03 -03:00
71b4211fc3 Missing categoria from cuentas 2021-12-23 01:09:35 -03:00
0378a2cf09 FIX: Double tables 2021-12-23 01:09:15 -03:00
4abe3448c0 Upgrades to the UI 2021-12-23 00:46:56 -03:00
9e29dd09b7 Remove ignored files 2021-12-23 00:09:23 -03:00
52443c2226 FIX: Clear form and table 2021-12-23 00:09:09 -03:00
572a9dc87c FIX: Renaming with correct extension 2021-12-23 00:08:49 -03:00
66882d9f85 FIX: Upload valid file check 2021-12-23 00:08:28 -03:00
1a0c83fb2b FIX: check if upload subfolder exists when loading files 2021-12-22 23:20:23 -03:00
a5428b252e FIX: Ignoring upload resources from ui 2021-12-22 23:11:51 -03:00
605c905f5d Python 2021-12-22 21:53:30 -03:00
93f77bfbb8 Upload files 2021-12-22 01:38:34 -03:00
fddba2fb87 Manage uploaded files 2021-12-22 01:38:23 -03:00
e6ebb2c279 FIX: url importar 2021-12-20 23:29:40 -03:00
45952bb3ac FIX: Select cuentas 2021-12-20 23:14:23 -03:00
894cc26b21 Upload files 2021-12-20 22:51:15 -03:00
64ffb53f0c Composer for UI 2021-12-20 22:45:00 -03:00
42310ef0e4 Formatting 2021-12-20 22:44:47 -03:00
a6362a6770 FIX: Empty results threw errors 2021-12-20 22:44:29 -03:00
e9c63abc3a PHP info 2021-12-20 22:43:43 -03:00
9f47c8a85f Default value for seed 2021-12-20 22:43:32 -03:00
34eedb93d7 FIX: .env not loaded in ui 2021-12-20 21:35:47 -03:00
960c418848 Python 2021-12-09 21:14:28 -03:00
0e5714edc8 FIX: cuentas 2021-12-07 09:13:20 -03:00
f33bddfbea Added Docker profiles 2021-12-06 22:22:54 -03:00
25f873c453 Python 2021-12-06 22:13:57 -03:00
34b429530f Python 2021-12-06 22:13:06 -03:00
9d2504f016 UI 2021-12-06 22:10:57 -03:00
8ef4ab1c7d API 2021-12-06 22:10:41 -03:00
10b2485cfd Ignore uploads 2021-12-06 22:10:30 -03:00
0382f8c286 Dockerfile 2021-12-06 22:10:12 -03:00
a3311f805e Env files samples 2021-12-06 22:08:48 -03:00
378de3ed86 Docker 2021-12-06 22:08:05 -03:00
69c2cffa6c Docker 2021-12-06 22:05:13 -03:00
61448a2521 Python working 2021-11-02 22:12:25 -03:00
b0f7c9b2b1 Cleanup 2021-11-02 22:12:11 -03:00
0c44554375 Camelot reading 2021-11-02 15:37:36 -03:00
5ee267568a PDF reading with python 2021-11-01 11:09:26 -03:00
237 changed files with 8691 additions and 1165 deletions

1
.api.env.sample Normal file
View File

@ -0,0 +1 @@
API_KEY=

1
.console.env.sample Normal file
View File

@ -0,0 +1 @@
API_URL=http://api-proxy

5
.db.env.sample Normal file
View File

@ -0,0 +1,5 @@
MYSQL_HOST=
MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE=
MYSQL_USER=
MYSQL_PASSWORD=

View File

@ -1,5 +1 @@
MYSQL_HOST=
MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE=
MYSQL_USER=
MYSQL_PASSWORD=
COMPOSE_PROFILES=

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
**/cache/
**/*.env
**/logs/
**/documents/
# Composer
**/vendor/

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

1
.python.env.sample Normal file
View File

@ -0,0 +1 @@
PYTHON_KEY=

27
Features.md Normal file
View File

@ -0,0 +1,27 @@
# Features
+ [ ] Multiples Cuentas
+ [ ] Giro, eg: Banco, Caja
+ [ ] Inversiones, eg: Crypto
+ [ ] Multiples Categorías
+ [ ] Ingresos, eg: Boletas, Facturas, Sueldo
+ [ ] Únicos
+ [ ] Recurrentes
+ [ ] Gastos, eg: Comida, Hogar, Movilización
+ [ ] Únicos
+ [ ] Recurrentes
+ [ ] Inversiones, eg: Bitcoin
+ [ ] Deudas
+ [ ] Nueva transacción
+ [ ] Leer cartolas
+ [ ] Leer cartolas en pdf
+ [ ] Leer cartolas en excel (xls, xlsx)
+ [ ] Obtener cartolas de emails
+ [ ] Ingresar cartolas a cuenta
+ [ ] Cuadrar cuentas
+ [ ] Reportes
+ [ ] Resultado
+ [ ] Mes
+ [ ] Línea de tiempo
+ [ ] Presupuesto
+ [ ] Por cuenta

11
TODO.md Normal file
View File

@ -0,0 +1,11 @@
# Contabilidad
1. Obtener pdf de cartolas desde email.
1. Conectar a Email por IMAP.
1. Buscar emails con cartolas.
1. Descargar cartolas.
1. Guardar de forma ordenada.
1. Extraer información e ingresar a base de datos a traves de API.
1. Abrir archivos y leer.
1. Formatear datos.
1. Mandar a API.

1
api/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
**/uploads/

View File

@ -1,5 +1,9 @@
FROM php:8-fpm
RUN docker-php-ext-install pdo pdo_mysql
RUN apt-get update -y && apt-get install -y git libzip-dev zip libpng-dev libfreetype6-dev libjpeg62-turbo-dev tesseract-ocr
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install pdo pdo_mysql zip gd
COPY --from=composer /usr/bin/composer /usr/bin/composer
WORKDIR /app

View File

@ -0,0 +1,186 @@
<?php
namespace ProVM\Alias\API;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use PDOException;
use function Safe\{error_log, json_decode};
use ProVM\Alias\Controller\Json;
use ProVM\Concept\API\Controller as ControllerInterface;
use ProVM\Concept\Repository;
abstract class Controller implements ControllerInterface
{
use Json;
public function __construct(ContainerInterface $container)
{
$this->setup($container);
}
abstract public function setup(ContainerInterface $container): void;
protected array $names;
public function setSingular(string $singular): ControllerInterface
{
$this->names['singular'] = $singular;
return $this;
}
public function setPlural(string $plural): ControllerInterface
{
$this->names['plural'] = $plural;
return $this;
}
public function getSingular(): string
{
return $this->names['singular'];
}
public function getPlural(): string
{
return $this->names['plural'];
}
protected Repository $repository;
public function setRepository(Repository $repository): ControllerInterface
{
$this->repository = $repository;
return $this;
}
public function getRepository(): Repository
{
return $this->repository;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$objs = [];
try {
$objs = $this->getRepository()->fetchAll();
} catch (PDOException $e) {
error_log($e);
}
return $this->withJson($response, [$this->getPlural() => $objs]);
}
public function get(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface
{
$obj = null;
try {
$obj = $this->getRepository()->fetchById($model_id);
} catch (PDOException $e) {
error_log($e);
}
return $this->withJson($response, [$this->getSingular() => $obj]);
}
public function add(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$body = $request->getBody();
$contents = $body->getContents();
$json = json_decode($contents)->{$this->getPlural()};
if (!is_array($json)) {
$json = [$json];
}
$output = [
'input' => $json,
$this->getPlural() => []
];
foreach ($json as $data) {
$obj = $this->getRepository()->create((array) $data);
$status = true;
$exists = true;
if ($obj->isNew()) {
$exists = false;
try {
$this->getRepository()->save($obj);
} catch (PDOException $e) {
error_log($e);
$status = false;
}
$output[$this->getPlural()] []= [
$this->getSingular() => $obj,
'exists' => $exists,
'added' => $status
];
}
}
return $this->withJson($response, $output);
}
public function edit(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$body = $request->getBody();
$contents = $body->getContents();
$json = json_decode($contents)->{$this->getPlural()};
if (!is_array($json)) {
$json = [$json];
}
$output = [
'input' => $json,
$this->getPlural() => []
];
foreach ($json as $data) {
$obj = $this->getRepository()->fetchById($data->id);
$old = clone $obj;
try {
$obj->edit((array) $data);
$status = $obj->isDirty();
if ($status) {
$this->getRepository()->save($obj);
}
} catch (PDOException $e) {
error_log($e);
$status = false;
}
$output[$this->getPlural()] []= [
'antes' => $old,
$this->getSingular() => $obj,
'edited' => $status
];
}
return $this->withJson($response, $output);
}
public function editOne(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface
{
$obj = $this->getRepository()->fetchById($model_id);
$body = $request->getBody();
$contents = $body->getContents();
$json = json_decode($contents, JSON_OBJECT_AS_ARRAY)[$this->getSingular()];
$output = [
'input' => $json,
'old' => clone $obj
];
try {
$obj->edit((array) $json);
$status = $obj->isDirty();
if ($status) {
$this->getRepository()->save($obj);
}
} catch (PDOException $e) {
error_log($e);
$status = false;
}
$output[$this->getSingular()] = $obj;
$output['edited'] = $status;
return $this->withJson($response, $output);
}
public function remove(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface
{
$output = [
$this->getSingular() => null,
'exists' => true,
'deleted' => false
];
try {
$obj = $this->getRepository()->fetchById($model_id);
$output[$this->getSingular()] = clone $obj;
try {
$this->repository->delete($obj);
} catch (PDOException $e) {
error_log($e);
$output['deleted'] = false;
}
} catch (PDOException $e) {
error_log($e);
$output['exists'] = false;
}
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Operation as OperationInterface;
use ProVM\Concept\API\Route\Parameter;
use ProVM\Concept\API\Route\Response;
abstract class Operation implements OperationInterface
{
protected array $tags;
public function setTags(array $tags): OperationInterface
{
foreach ($tags as $tag) {
$this->addTag($tag);
}
return $this;
}
public function addTag(string $tag): OperationInterface
{
$this->tags []= $tag;
return $this;
}
public function getTags(): array
{
return $this->tags;
}
protected string $summary;
public function setSummary(string $summary): OperationInterface
{
$this->summary = $summary;
return $this;
}
public function getSummary(): string
{
return $this->summary;
}
protected string $description;
public function setDescription(string $description): OperationInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected array $parameters;
public function setParameters(array $parameters): OperationInterface
{
foreach ($parameters as $parameter) {
$this->addParameter($parameter);
}
return $this;
}
public function addParameter(Parameter $parameter): OperationInterface
{
$this->parameters []= $parameter;
return $this;
}
public function getParameters(): array
{
return $this->parameters;
}
protected array $responses;
public function setResponses(array $responses): OperationInterface
{
foreach ($responses as $code => $response) {
$this->addResponse($code, $response);
}
return $this;
}
public function addResponse(int $code, Response $response): OperationInterface
{
$this->responses[$code] = $response;
return $this;
}
public function getResponses(): array
{
return $this->responses;
}
protected bool $deprecated;
public function setDeprecated(): OperationInterface
{
$this->deprecated = true;
return $this;
}
public function isDeprecated(): bool
{
return $this->deprecated ?? false;
}
public function jsonSerialize(): mixed
{
$arr = [];
$fields = ['tags', 'summary', 'description', 'parameters', 'responses'];
foreach ($fields as $field) {
if (isset($this->{$field})) {
$method = 'get' . ucwords($field);
$arr[$field] = $this->{$method}();
}
}
if ($this->isDeprecated()) {
$arr['deprecated'] = true;
}
return $arr;
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Parameter as ParameterInterface;
abstract class Parameter implements ParameterInterface
{
protected string $name;
public function setName(string $name): ParameterInterface
{
$this->name = $name;
return $this;
}
public function getName(): string
{
return $this->name;
}
protected string $description;
public function setDescription(string $description): ParameterInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected string $in;
public function setIn(string $in): ParameterInterface
{
$this->in = $in;
return $this;
}
public function getIn(): string
{
return $this->in;
}
protected bool $required;
public function setRequired(): ParameterInterface
{
$this->required = true;
return $this;
}
public function isRequired(): bool
{
return $this->required ?? false;
}
protected bool $deprecated;
public function setDeprecated(): ParameterInterface
{
$this->deprecated = true;
return $this;
}
public function isDeprecated(): bool
{
return $this->deprecated ?? false;
}
public function jsonSerialize(): mixed
{
$arr = [
'name' => $this->getName(),
'in' => $this->getIn(),
'description' => $this->getDescription()
];
if ($this->isRequired()) {
$arr['required'] = true;
}
if ($this->isDeprecated()) {
$arr['deprecated'] = true;
}
return $arr;
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Response as ResponseInterface;
abstract class Response implements ResponseInterface
{
protected string $description;
public function setDescription(string $description): ResponseInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected array $headers;
public function setHeaders(array $headers): ResponseInterface
{
foreach ($headers as $header) {
$this->addHeader($header);
}
return $this;
}
public function addHeader(string $header): ResponseInterface
{
$this->headers []= $header;
return $this;
}
public function getHeaders(): array
{
return $this->headers;
}
protected array $contents;
public function setContent(array $contents): ResponseInterface
{
foreach ($contents as $content) {
$this->addContent($content);
}
return $this;
}
public function addContent(string $content): ResponseInterface
{
$this->contents []= $content;
return $this;
}
public function getContents(): array
{
return $this->contents;
}
public function jsonSerialize(): mixed
{
$arr = [
'description' => $this->getDescription()
];
if (isset($this->headers) and count($this->headers) > 0) {
$arr['headers'] = $this->getHeaders();
}
if (isset($this->contents) and count($this->contents) > 0) {
$arr['content'] = $this->getContents();
}
return $arr;
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Route as RouteInterface;
use ProVM\Concept\API\Route\Operation;
use ProVM\Concept\API\Route\Parameter;
abstract class Route implements RouteInterface
{
protected string $ref;
public function setRef(string $ref): RouteInterface
{
$this->ref = $ref;
return $this;
}
public function getRef(): string
{
return $this->ref;
}
protected string $summary;
public function setSummary(string $summary): RouteInterface
{
$this->summary = $summary;
return $this;
}
public function getSummary(): string
{
return $this->summary;
}
protected string $description;
public function setDescription(string $description): RouteInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected array $methods;
public function setMethods(array $methods): RouteInterface
{
foreach ($methods as $method => $operation) {
$this->addMethod($method, $operation);
}
return $this;
}
public function addMethod(string $method, Operation $operation): RouteInterface
{
$this->methods[$method] = $operation;
return $this;
}
public function getMethods(): array
{
return $this->methods;
}
protected array $parameters;
public function setParameters(array $parameters): RouteInterface
{
foreach ($parameters as $parameter) {
$this->addParameter($parameter);
}
return $this;
}
public function addParameter(Parameter $parameter): RouteInterface
{
$this->parameters []= $parameter;
return $this;
}
public function getParameters(): array
{
return $this->parameters;
}
public function jsonSerialize(): mixed
{
$arr = [];
if (isset($this->ref)) {
$arr['ref'] = $this->getRef();
}
if (isset($this->summary)) {
$arr['summary'] = $this->getSummary();
}
if (isset($this->description)) {
$arr['description'] = $this->getDescription();
}
foreach ($this->methods as $method => $operation) {
$arr[$method] = $operation;
}
if (isset($this->parameters)) {
$arr['parameters'] = $this->getParameters();
}
return $arr;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace ProVM\Alias\Controller;
use Psr\Http\Message\ResponseInterface;
use function Safe\json_encode;
trait Json
{
public function withJson(ResponseInterface $response, $json_data, int $status_code = 200): ResponseInterface
{
$response->getBody()->write(json_encode($json_data));
return $response
->withStatus($status_code)
->withHeader('Content-Type', 'application/json');
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace ProVM\Alias;
use Psr\Database\DatabaseInterface;
use Exception;
use PDO, PDOException, PDOStatement;
abstract class Database implements DatabaseInterface
{
protected string $host;
protected int $port;
public function setHost(string $host, ?int $port = null): DatabaseInterface
{
$this->host = $host;
if ($port !== null) {
$this->port = $port;
}
return $this;
}
public function getHost(): string
{
return $this->host;
}
public function getPort(): int
{
return $this->port;
}
protected string $name;
public function setName(string $database_name): DatabaseInterface
{
$this->name = $database_name;
return $this;
}
public function getName(): string
{
return $this->name;
}
protected string $username;
protected string $password;
public function setUser(string $username, string $password): DatabaseInterface
{
$this->username = $username;
$this->password = $password;
return $this;
}
public function getUser(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
protected PDO $connection;
public function connect(): DatabaseInterface
{
if ($this->needsUser()) {
$this->connection = new PDO($this->getDsn(), $this->getUser(), $this->getPassword());
return $this;
}
$this->connection = new PDO($this->getDsn());
return $this;
}
public function getConnection(): PDO
{
if (!isset($this->connection)) {
return $this->connect()->connection;
}
return $this->connection;
}
public function query(string $query): array
{
$st = $this->getConnection()->query($query);
if (!$st) {
throw new PDOException("Could not retrieve anything with '{$query}'.");
}
$results = $st->fetchAll(PDO::FETCH_ASSOC);
if (!$results) {
throw new PDOException('Could not retrieve any results.');
}
return $results;
}
public function beginTransaction(): void
{
if (!$this->getConnection()->beginTransaction()) {
throw new PDOException('Could not begin transaction.');
}
}
public function commit(): void
{
if (!$this->getConnection()->commit()) {
throw new PDOException('Could not commit');
}
}
public function rollBack(): void
{
if (!$this->getConnection()->rollBack()) {
throw new PDOException('Could not rollback.');
}
}
protected PDOStatement $prepared_statement;
public function prepare(string $query): DatabaseInterface
{
$st = $this->getConnection()->prepare($query);
if (!$st) {
throw new PDOException("Could not prepare query '{$query}'.");
}
$this->prepared_statement = $st;
return $this;
}
public function execute(array $data): array
{
if (!isset($this->prepared_statement)) {
throw new Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($data)) {
throw new PDOException('Could not execute prepared statement.');
}
$results = $this->prepared_statement->fetchAll(PDO::FETCH_ASSOC);
if (!$results) {
throw new PDOException('Could not retrieve any results.');
}
return $results;
}
public function insert(array $values): void
{
if (!isset($this->prepared_statement)) {
throw new Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($values)) {
throw new PDOException('Could not insert.');
}
}
public function update(array $data): void
{
if (!isset($this->prepared_statement)) {
throw new Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($data)) {
throw new PDOException('Could not update.');
}
}
public function delete(array $data): void
{
if (!isset($this->prepared_statement)) {
throw new Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($data)) {
throw new PDOException('Could not delete.');
}
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace ProVM\Alias\Database;
use Error;
use function Safe\error_log;
use ProVM\Concept\Database\Query as QueryInterface;
abstract class Query implements QueryInterface
{
protected array $errors;
protected function error(Error $error): Query
{
$this->errors []= $error;
return $this;
}
protected function log(): void
{
if (!isset($this->errors) or count($this->errors) === 0) {
return;
}
$message = ['Query Error'];
foreach ($this->errors as $error) {
$message []= $error->getMessage();
}
error_log(implode(PHP_EOL, $message));
}
public function __toString(): string
{
return $this->build();
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace ProVM\Alias\Factory;
use Psr\Container\ContainerInterface;
use ProVM\Concept\Factory\Model as FactoryInterface;
use ProVM\Concept\Repository;
abstract class Model implements FactoryInterface
{
protected ContainerInterface $container;
public function setContainer(ContainerInterface $container): Model
{
$this->container = $container;
return $this;
}
public function getContainer(): ContainerInterface
{
return $this->container;
}
public function find(string $model_name): Repository
{
$class = str_replace('Model', 'Repository', $model_name);
return $this->getContainer()->get($class);
}
}

46
api/ProVM/Alias/File.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace ProVM\Alias;
use function Safe\{fopen,fclose,fwrite};
use ProVM\Concept\File as FileInterface;
abstract class File implements FileInterface
{
protected string $filename;
public function setFilename(string $filename): FileInterface
{
$this->filename = $filename;
return $this;
}
public function getFilename(): string
{
return $this->filename;
}
public function isDir(): bool
{
return is_dir($this->getFilename());
}
public function isReadable(): bool
{
return is_readable($this->getFilename());
}
public function isWriteable(): bool
{
return is_writeable($this->getFilename());
}
public function read(?int $length = null): string
{
$fh = fopen($this->getFilename(), 'r');
$str = fgets($fh, $length);
fclose($fh);
return $str;
}
public function write(string $data, ?int $length = null): void
{
$fh = fopen($this->getFilename(), 'r');
fwrite($fh, $data, $length);
fclose($fh);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace ProVM\Alias;
use function Safe\{touch,mkdir,unlink};
use ProVM\Concept\Filesystem as FilesystemInterface;
use ProVM\Implement\Path;
abstract class Filesystem implements FilesystemInterface
{
protected string $base_path;
public function setBasePath(string $path): FilesystemInterface
{
$this->base_path = $path;
return $this;
}
public function getBasePath(): string
{
return $this->base_path;
}
public function buildPath(string $relative_path): string
{
return Path::fromArray([
$this->getBasePath(),
$relative_path
]);
}
public function has(string $relative_path): bool
{
return file_exists($this->buildPath($relative_path));
}
public function mkdir(string $relative_path): void
{
mkdir($this->buildPath($relative_path));
}
public function create(string $relative_path): void
{
touch($this->buildPath($relative_path));
}
public function delete(string $relative_path): void
{
unlink($this->buildPath($relative_path));
}
}

90
api/ProVM/Alias/Model.php Normal file
View File

@ -0,0 +1,90 @@
<?php
namespace ProVM\Alias;
use ReflectionObject;
use ProVM\Concept\Model as ModelInterface;
abstract class Model implements ModelInterface
{
protected int $id;
public function setId(int $id): ModelInterface
{
$this->id = $id;
return $this;
}
public function getId(): int
{
return $this->id;
}
protected bool $new = false;
public function setNew(): ModelInterface
{
$this->new = true;
return $this;
}
public function isNew(): bool
{
return $this->new;
}
protected array $edited;
public function setEdited(string $property): ModelInterface
{
$this->edited []= $property;
return $this;
}
public function getEdited(): array
{
return $this->edited;
}
public function isDirty(): bool
{
return isset($this->edited) and count($this->edited) > 0;
}
public function edit(array $data): void
{
$this->edited = [];
foreach ($data as $key => $val) {
if ($key === 'id') {
continue;
}
$getter = Model::parseGetter($key);
$setter = Model::parseSetter($key);
if (method_exists($this, $getter) and $this->{$getter}() !== $val) {
$this->{$setter}($val);
$this->setEdited($key);
}
}
}
public static function parseSetter(string $property): string
{
return 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $property)));
}
public static function parseGetter(string $property): string
{
return 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $property)));
}
public function jsonSerialize(): mixed
{
$ref = new ReflectionObject($this);
$properties = ($ref->getProperties());
$output = [
'id' => $this->getId()
];
foreach ($properties as $property) {
if (get_called_class() !== $property->getDeclaringClass()->getName()) {
continue;
}
$key = $property->getName();
$method = Model::parseGetter($key);
if (method_exists($this, $method)) {
$output[$key] = $this->{$method}();
}
}
return $output;
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace ProVM\Alias;
use Psr\Database\DatabaseInterface;
use Error;
use function Safe\error_log;
use ProVM\Concept\Model;
use ProVM\Concept\Repository as RepositoryInterface;
use ProVM\Concept\Factory\Model as FactoryInterface;
use ProVM\Implement\Database\QueryBuilder;
abstract class Repository implements RepositoryInterface
{
public function __construct(DatabaseInterface $database, QueryBuilder $builder, FactoryInterface $factory)
{
$this->setDatabase($database);
$this->setQueryBuilder($builder);
$this->setFactory($factory);
$this->setProperties(['id']);
$this->setup();
}
abstract public function setup(): void;
protected QueryBuilder $query;
public function setQueryBuilder(QueryBuilder $builder): RepositoryInterface
{
$this->query = $builder;
return $this;
}
public function getQueryBuilder(): QueryBuilder
{
return $this->query;
}
protected FactoryInterface $factory;
public function setFactory(FactoryInterface $factory): RepositoryInterface
{
$this->factory = $factory;
return $this;
}
public function getFactory(): FactoryInterface
{
return $this->factory;
}
protected string $table;
public function setTable(string $table): RepositoryInterface
{
$this->table = $table;
return $this;
}
public function getTable(): string
{
return $this->table;
}
protected array $properties;
public function addProperty(string $name): RepositoryInterface
{
$this->properties []= $name;
return $this;
}
public function setProperties(array $properties): RepositoryInterface
{
$this->properties = [];
return $this->addProperties($properties);
}
public function addProperties(array $properties): RepositoryInterface
{
foreach ($properties as $property) {
$this->addProperty($property);
}
return $this;
}
public function getProperties(): array
{
return $this->properties;
}
protected DatabaseInterface $database;
public function setDatabase(DatabaseInterface $database): RepositoryInterface
{
$this->database = $database;
return $this;
}
public function getDatabase(): DatabaseInterface
{
return $this->database;
}
public function fetchAll(): array
{
$query = $this->getQueryBuilder()->select()->from($this->getTable())->build();
return array_map([$this, 'load'], $this->getDatabase()->query($query));
}
public function fetchById(int $id): Model
{
$query = $this->getQueryBuilder()->select()->from($this->getTable())->where('id = ?');
return $this->load($this->getDatabase()->prepare($query)->execute([$id])[0]);
}
protected function extractProperties(Model $model): array
{
$properties = [];
foreach ($this->getProperties() as $p) {
if ($model->isNew() and $p === 'id') {
continue;
}
$method = $model::parseGetter($p);
if (method_exists($model, $method)) {
try {
$properties[$p] = $model->{$method}();
} catch (Error $e) {
error_log($e);
}
}
}
return $properties;
}
public function save(Model $model): void
{
if ($model->isNew()) {
$this->saveNew($model);
return;
}
if ($model->isDirty()) {
$this->saveUpdate($model);
}
}
protected function saveNew(Model $model): void
{
$properties = $this->extractProperties($model);
$columns = array_keys($properties);
$ivalues = array_map(function($item) {return '?';}, array_keys($properties));
$values = array_values($properties);
$query = $this->getQueryBuilder()->insert()->into($this->getTable())->columns($columns)->values($ivalues)->build();
$this->getDatabase()->prepare($query)->insert($values);
}
protected function saveUpdate(Model $model): void
{
$edited = $model->getEdited();
$properties = array_intersect_key($this->extractProperties($model), array_combine($edited, $edited));
$sets = array_map(function($item) {
return "{$item} = ?";
}, array_keys($properties));
$values = array_values($properties);
$query = $this->getQueryBuilder()->update()->table($this->getTable())->set($sets)->where('id = ?')->build();
$this->getDatabase()->prepare($query)->update(array_merge($values, [$model->getId()]));
}
public function delete(Model $model): void
{
$query = $this->getQueryBuilder()->delete()->from($this->getTable())->where('id = ?')->build();
$this->getDatabase()->prepare($query)->delete([$model->getId()]);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace ProVM\Concept\API;
use ProVM\Concept\Repository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
interface Controller
{
public function setSingular(string $singular): Controller;
public function setPlural(string $plural): Controller;
public function getSingular(): string;
public function getPlural(): string;
public function setRepository(Repository $repository): Controller;
public function getRepository(): Repository;
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface;
public function get(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface;
public function add(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface;
public function edit(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface;
public function editOne(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface;
public function remove(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface;
}

View File

@ -0,0 +1,7 @@
<?php
namespace ProVM\Concept\API;
interface OpenAPI
{
public static function add(array $data);
}

View File

@ -0,0 +1,24 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Operation extends JsonSerializable, OpenAPI
{
public function setTags(array $tags): Operation;
public function addTag(string $tag): Operation;
public function getTags(): array;
public function setSummary(string $summary): Operation;
public function getSummary(): string;
public function setDescription(string $description): Operation;
public function getDescription(): string;
public function setParameters(array $parameters): Operation;
public function addParameter(Parameter $parameter): Operation;
public function getParameters(): array;
public function setResponses(array $responses): Operation;
public function addResponse(int $code, Response $response): Operation;
public function getResponses(): array;
public function setDeprecated(): Operation;
public function isDeprecated(): bool;
}

View File

@ -0,0 +1,19 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Parameter extends JsonSerializable, OpenAPI
{
public function setName(string $name): Parameter;
public function getName(): string;
public function setIn(string $in): Parameter;
public function getIn(): string;
public function setDescription(string $description): Parameter;
public function getDescription(): string;
public function setRequired(): Parameter;
public function isRequired(): bool;
public function setDeprecated(): Parameter;
public function isDeprecated(): bool;
}

View File

@ -0,0 +1,17 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Response extends JsonSerializable, OpenAPI
{
public function setDescription(string $description): Response;
public function getDescription(): string;
public function setHeaders(array $headers): Response;
public function addHeader(string $header): Response;
public function getHeaders(): array;
public function setContent(array $contents): Response;
public function addContent(string $content): Response;
public function getContents(): array;
}

View File

@ -0,0 +1,21 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Route extends JsonSerializable, OpenAPI
{
public function setRef(string $ref): Route;
public function getRef(): string;
public function setSummary(string $summary): Route;
public function getSummary(): string;
public function setDescription(string $description): Route;
public function getDescription(): string;
public function setMethods(array $methods): Route;
public function addMethod(string $method, Operation $operation): Route;
public function getMethods(): array;
public function setParameters(array $parameters): Route;
public function addParameter(Parameter $parameter): Route;
public function getParameters(): array;
}

View File

@ -0,0 +1,7 @@
<?php
namespace ProVM\Concept\Database;
interface Query
{
public function build(): string;
}

View File

@ -0,0 +1,10 @@
<?php
namespace ProVM\Concept\Factory;
use ProVM\Concept\Model as ModelInterface;
use ProVM\Concept\Repository;
interface Model
{
public function find(string $model_name): Repository;
}

View File

@ -0,0 +1,16 @@
<?php
namespace ProVM\Concept;
interface File
{
public function setFilename(string $filename): File;
public function getFilename(): string;
public function isDir(): bool;
public function isReadable(): bool;
public function isWriteable(): bool;
public function read(?int $length = null): string;
public function write(string $data, ?int $length = null);
public function append(string $data, ?int $length = null);
}

View File

@ -0,0 +1,17 @@
<?php
namespace ProVM\Concept;
interface Filesystem
{
public function setBasePath(string $path): Filesystem;
public function getBasePath(): string;
public function buildPath(string $relative_path): string;
public function has(string $relative_path): bool;
public function mkdir(string $relative_path): void;
public function create(string $relative_path): void;
public function get(string $relative_path): File;
public function delete(string $relative_path): void;
}

View File

@ -0,0 +1,16 @@
<?php
namespace ProVM\Concept;
use JsonSerializable;
interface Model extends JsonSerializable
{
public function setNew(): Model;
public function isNew(): bool;
public function setEdited(string $property): Model;
public function getEdited(): array;
public function isDirty(): bool;
public function edit(array $data): void;
public static function parseSetter(string $property): string;
public static function parseGetter(string $property): string;
}

View File

@ -0,0 +1,15 @@
<?php
namespace ProVM\Concept;
use ArrayAccess;
interface Path extends ArrayAccess
{
public function compose(string $base_path): Path;
public function add(string $path_segment): Path;
public function build(): string;
public static function fromArray(array $path): string;
public static function isAbsolute(string $path): bool;
public static function isRelative(string $path): bool;
}

View File

@ -0,0 +1,12 @@
<?php
namespace ProVM\Concept;
interface Repository
{
public function fetchAll(): array;
public function fetchById(int $id): Model;
public function load(array $data_row): Model;
public function create(array $data): Model;
public function save(Model $model): void;
public function delete(Model $model): void;
}

View File

@ -0,0 +1,37 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Operation as BaseOperation;
class Operation extends BaseOperation
{
public static function add(array $data): Operation
{
$operation = new Operation();
if (isset($data['tags'])) {
$operation->setTags($data['tags']);
}
if (isset($data['summary'])) {
$operation->setSummary($data['summary']);
}
if (isset($data['description'])) {
$operation->setDescription($data['description']);
}
if (isset($data['parameters'])) {
foreach ($data['parameters'] as $p) {
$parameter = Parameter::add($p);
$operation->addParameter($parameter);
}
}
if (isset($data['responses'])) {
foreach ($data['responses'] as $code => $r) {
$response = Response::add($r);
$operation->addResponse($code, $response);
}
}
if (isset($data['deprecated'])) {
$operation->setDeprecated();
}
return $operation;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Parameter as BaseParameter;
class Parameter extends BaseParameter
{
public static function add(array $data): Parameter
{
$parameter = new Parameter();
$parameter->setName($data['name']);
$parameter->setIn($data['in']);
if (isset($data['description'])) {
$parameter->setDescription($data['description']);
}
if (isset($data['required'])) {
$parameter->setRequired();
}
if (isset($data['deprecated'])) {
$parameter->setDeprecated();
}
return $parameter;
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Response as BaseResponse;
class Response extends BaseResponse
{
public static function add(array $data): Response
{
$response = new Response();
$response->setDescription($data['description']);
if (isset($data['headers'])) {
$response->setHeaders($data['headers']);
}
if (isset($data['content'])) {
$response->setContent($data['content']);
}
return $response;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Route as BaseRoute;
class Route extends BaseRoute
{
public static function add(array $data): Route
{
$route = new Route();
if (isset($data['ref'])) {
$route->setRef($data['ref']);
}
if (isset($data['summary'])) {
$route->setSummary($data['summary']);
}
if (isset($data['description'])) {
$route->setDescription($data['description']);
}
$methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
foreach ($methods as $method) {
if (isset($data[$method])) {
$operation = Operation::add($data[$method]);
$route->addMethod($method, $operation);
}
}
if (isset($data['parameters'])) {
foreach ($data['parameters'] as $p) {
$parameter = Parameter::add($p);
$operation->addParameter($parameter);
}
}
return $route;
}
}

View File

@ -0,0 +1,118 @@
<?php
namespace ProVM\Implement;
use Psr\Collection\CollectionInterface;
use ReflectionFunction;
class Collection implements CollectionInterface
{
public function __construct()
{
$this->set(CollectionInterface::class, $this);
}
protected array $data;
public function set(mixed $name, $value): CollectionInterface
{
$this->data[$name] = $value;
return $this;
}
public function has(mixed $name): bool
{
return isset($this->data[$name]);
}
public function get(mixed $name)
{
return $this->process($this->data[$name]) ?? null;
}
public function remove(mixed $name): CollectionInterface
{
unset($this->data[$name]);
return $this;
}
protected function process($value)
{
if (is_callable($value)) {
return $this->processCallable($value);
}
if (is_array($value)) {
return $this->processArray($value);
}
return $value;
}
protected function processCallable(Callable $value)
{
$ref = new ReflectionFunction($value);
$param_array = [];
$params = $ref->getParameters();
foreach ($params as $p) {
$param_array[$p->getName()] = $this->get($p->getType()->getName());
}
return $ref->invokeArgs($param_array);
}
protected function processArray(array $value)
{
return $value;
}
public static function fromArray(array $source): CollectionInterface
{
$obj = new Collection();
foreach ($source as $key => $value) {
$obj->set($key, $value);
}
return $obj;
}
public static function fromObject(object $source): CollectionInterface
{
return Collection::fromArray((array) $source);
}
public function __get(mixed $name): mixed
{
return $this->get($name);
}
public function current(): mixed
{
return $this->data[$this->key()];
}
public function next(): void
{
next($this->data);
}
public function key(): mixed
{
return key($this->data);
}
public function valid(): bool
{
return isset($this->data);
}
public function rewind(): void
{
reset($this->data);
}
public function offsetExists(mixed $offset): bool
{
return $this->has($offset);
}
public function offsetGet(mixed $offset): mixed
{
return $this->get($offset);
}
public function offsetSet(mixed $offset, mixed $value): void
{
$this->set($offset, $value);
}
public function offsetUnset(mixed $offset): void
{
$this->remove($offset);
}
public function count(): int
{
return count($this->data);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace ProVM\Implement\Database;
use ProVM\Alias\Database;
class MySQL extends Database
{
public function __construct(string $host, string $username, string $password, string $database_name, int $port = 3306)
{
$this->setHost($host, $port);
$this->setUser($username, $password);
$this->setName($database_name);
}
public function getDsn(): string
{
$dsn = ["mysql:host={$this->getHost()}"];
if ($this->getPort() !== 3306) {
$dsn []= "port={$this->getPort()}";
}
$dsn []= "dbname={$this->getName()}";
return implode(';', $dsn);
}
public function needsUser(): bool
{
return true;
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use ProVM\Alias\Database\Query;
class Delete extends Query
{
protected string $table;
public function from($table): Delete
{
$this->table = $table;
return $this;
}
public function getFrom(): string
{
return $this->table;
}
protected array $conditions;
public function where(array|string $conditions): Delete
{
if (is_string($conditions)) {
return $this->addCondition($conditions);
}
foreach ($conditions as $condition) {
$this->addCondition($condition);
}
return $this;
}
public function addCondition(string $condition): Delete
{
$this->conditions []= $condition;
return $this;
}
public function getWhere(): string
{
return implode(' AND ', $this->conditions);
}
public function build(): string
{
$output = ['DELETE FROM'];
$output []= $this->getFrom();
$output []= "WHERE {$this->getWhere()}";
return implode(' ', $output);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use Error;
use ProVM\Alias\Database\Query;
class Insert extends Query
{
protected string $table;
public function into(string $table): Insert
{
$this->table = $table;
return $this;
}
public function getTable(): string
{
return $this->table;
}
protected Select $select;
public function select(Select $select): Insert
{
$this->select = $select;
return $this;
}
public function getSelect(): string
{
return $this->select->build();
}
protected array $columns;
public function columns(array $columns): Insert
{
foreach ($columns as $column) {
$this->addColumn($column);
}
return $this;
}
public function addColumn(string $column): Insert
{
$this->columns []= $column;
return $this;
}
public function getColumns(): string
{
return implode(', ', array_map(function($item) {return "`{$item}`";}, $this->columns));
}
protected array $values;
public function values(array $values): Insert
{
foreach ($values as $value) {
$this->addValue($value);
}
return $this;
}
public function addValue(string $value): Insert
{
$this->values []= $value;
return $this;
}
public function getValues(): string
{
return implode(', ', array_map(function($item) {
return $item;
}, $this->values));
}
protected array $updates;
public function update(array $updates): Insert
{
foreach ($updates as $update) {
$this->addUpdate($update);
}
return $this;
}
public function addUpdate(string $update_string): Insert
{
$this->updates []= $update_string;
return $this;
}
public function getUpdates(): string
{
return implode(', ', $this->updates);
}
public function build(): string
{
$output = ['INSERT INTO'];
$output []= $this->getTable();
try {
$output []= "({$this->getColumns()})";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= $this->getSelect();
} catch (\Error $e) {
$output []= "VALUES ({$this->getValues()})";
}
try {
$output []= "ON DUPLICATE KEY UPDATE {$this->getUpdates()}";
} catch (Error $e) {
$this->error($e);
}
$this->log();
return implode(' ', $output);
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use Error;
use function Safe\error_log;
use ProVM\Alias\Database\Query;
class Select extends Query
{
protected array $columns;
public function select(array|string $columns): Select
{
if (is_string($columns)) {
return $this->addColumn($columns);
}
foreach ($columns as $column) {
$this->addColumn($column);
}
return $this;
}
public function addColumn(string $column): Select
{
$this->columns []= $column;
return $this;
}
public function getSelect(): string
{
return implode(', ', array_map(function($item) {
return "`{$item}`";
}, $this->columns));
}
protected string $from;
public function from(string $table): Select
{
$this->from = $table;
return $this;
}
public function getFrom(): string
{
return $this->from;
}
protected array $joins;
public function join(array $joins): Select
{
if (is_string($joins[0])) {
return $this->addJoin($joins[0], $joins[1], $joins[2]);
}
foreach ($joins as $join) {
$this->addJoin($join[0], $join[1], $join[2]);
}
return $this;
}
public function addJoin(string $join_type, string $join_table, string $conditions): Select
{
$join_type = strtoupper($join_type);
$this->joins []= "{$join_type} {$join_table} {$conditions}";
return $this;
}
public function getJoins(): string
{
return implode('', $this->joins);
}
protected array $conditions;
public function where(array|string $conditions): Select
{
if (is_string($conditions)) {
return $this->addWhere($conditions);
}
foreach ($conditions as $condition) {
$this->addWhere($condition);
}
return $this;
}
public function addWhere($condition): Select
{
$this->conditions []= $condition;
return $this;
}
public function getWheres(): string
{
return implode(' AND ', $this->conditions);
}
protected array $orders;
public function order(array|string $orders): Select
{
if (is_string($orders)) {
return $this->addOrder($orders);
}
foreach ($orders as $order) {
if (is_array($order)) {
$this->addOrder($order[0], $order[1]);
continue;
}
$this->addOrder($order);
}
return $this;
}
public function addOrder(string $order, string $dir = 'desc'): Select
{
$dir = strtoupper($dir);
$this->orders []= "{$order} {$dir}";
return $this;
}
public function getOrders(): string
{
return implode(', ', $this->orders);
}
protected array $groups;
public function group(array|string $groups): Select
{
if (is_string($groups)) {
return $this->addGroup($groups);
}
foreach ($groups as $group) {
$this->addGroup($group);
}
return $this;
}
public function addGroup(string $group): Select
{
$this->groups []= $group;
return $this;
}
public function getGroups(): string
{
return implode(', ', $this->groups);
}
protected array $having;
public function having(array|string $havings): Select
{
if (is_string($havings)) {
return $this->addHaving($havings);
}
foreach ($havings as $having) {
$this->addHaving($having);
}
return $this;
}
public function addHaving(string $having): Select
{
$this->having []= $having;
return $this;
}
public function getHaving(): string
{
return implode(' AND ', $this->having);
}
protected int $limit;
public function limit(int $limit): Select
{
$this->limit = $limit;
return $this;
}
protected int $offset;
public function offset(int $offset): Select
{
$this->offset = $offset;
return $this;
}
public function getLimit(): string
{
$output = [$this->limit];
if (isset($this->offset)) {
$output []= 'OFFSET';
$output []= $this->offset;
}
return implode(' ', $output);
}
public function build(): string
{
$output = ['SELECT'];
try {
$output []= $this->getSelect();
} catch (Error $e) {
$output []= '*';
}
$output []= 'FROM';
$output []= $this->getFrom();
try {
$output [] = $this->getJoins();
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "WHERE {$this->getWheres()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "GROUP BY {$this->getGroups()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "HAVING {$this->getHaving()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "ORDER BY {$this->getOrders()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "LIMIT {$this->getLimit()}";
} catch (Error $e) {
$this->error($e);
}
$this->log();
return implode(' ', $output);
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use ProVM\Alias\Database\Query;
class Update extends Query
{
protected string $table;
public function table(string $table): Update
{
$this->table = $table;
return $this;
}
public function getTable(): string
{
return $this->table;
}
protected array $sets;
public function set(array|string $set_strings): Update
{
if (is_string($set_strings)) {
return $this->addSet($set_strings);
}
foreach ($set_strings as $set_string) {
$this->addSet($set_string);
}
return $this;
}
public function addSet(string $set_string): Update
{
$this->sets []= $set_string;
return $this;
}
public function getSets(): string
{
return implode(', ', $this->sets);
}
protected array $conditions;
public function where(array|string $conditions): Update
{
if (is_string($conditions)) {
return $this->addCondition($conditions);
}
foreach ($conditions as $condition) {
$this->addCondition($condition);
}
return $this;
}
public function addCondition(string $condition): Update
{
$this->conditions []= $condition;
return $this;
}
public function getWhere(): string
{
return implode(' AND ', $this->conditions);
}
public function build(): string
{
$output = ['UPDATE'];
$output []= $this->getTable();
$output []= "SET {$this->getSets()}";
$output []= "WHERE {$this->getWhere()}";
return implode(' ', $output);
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace ProVM\Implement\Database;
use Psr\Container\ContainerInterface;
use ProVM\Concept\Database\Query;
use ProVM\Implement\Database\Query\MySQL\Delete;
use ProVM\Implement\Database\Query\MySQL\Insert;
use ProVM\Implement\Database\Query\MySQL\Select;
use ProVM\Implement\Database\Query\MySQL\Update;
class QueryBuilder
{
public function __construct(ContainerInterface $container)
{
$arr = [
'select',
'insert',
'update',
'delete'
];
foreach ($arr as $b) {
$builder = implode("\\", [
'ProVM',
'Implement',
'Database',
'Query',
'MySQL',
ucwords($b)
]);
$this->addBuilder($container->get($builder));
}
}
protected array $builders;
public function addBuilder(Query $builder): QueryBuilder
{
$name = explode("\\", get_class($builder));
$name = array_pop($name);
$this->builders[strtolower($name)] = $builder;
return $this;
}
public function start(string $name): Query
{
return $this->builders[strtolower($name)];
}
public function select(): Select
{
return $this->start('select');
}
public function insert(): Insert
{
return $this->start('insert');
}
public function update(): Update
{
return $this->start('update');
}
public function delete(): Delete
{
return $this->start('delete');
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ProVM\Implement\Factory;
use Psr\Container\ContainerInterface;
use ProVM\Alias\Factory\Model as BaseModel;
class Model extends BaseModel
{
public function __construct(ContainerInterface $container)
{
$this->setContainer($container);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace ProVM\Implement\Factory;
use Psr\Container\ContainerInterface;
use ProVM\Concept\Repository as RepositoryInterface;
class Repository
{
protected ContainerInterface $container;
public function setContainer(ContainerInterface $container): Repository
{
$this->container = $container;
return $this;
}
public function getContainer(): ContainerInterface
{
return $this->container;
}
protected string $namespace;
public function setNamespace(string $namespace): Repository
{
$this->namespace = $namespace;
return $this;
}
public function getNamespace(): string
{
return $this->namespace;
}
public function find(string $repository_class): RepositoryInterface
{
$class = implode("\\", [
$this->getNamespace(),
$repository_class
]);
return $this->getContainer()->get($class);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace ProVM\Implement;
use ProVM\Alias\File as BaseFile;
class File extends BaseFile
{
public function __construct(string $filename)
{
$this->setFilename($filename);
}
public function append(string $data, ?int $length = null)
{
$this->write($data, $length);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace ProVM\Implement;
use ProVM\Concept\File as FileInterface;
use ProVM\Alias\Filesystem as BaseFilesystem;
class Filesystem extends BaseFilesystem
{
public function __construct(string $path)
{
$this->setBasePath($path);
}
public function get(string $relative_path): FileInterface
{
return new File($this->buildPath($relative_path));
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace ProVM\Implement;
use ProVM\Concept\Path as PathInterface;
class Path implements PathInterface
{
protected array $path;
public function compose(string $base_path): PathInterface
{
$this->path = [];
return $this->add($base_path);
}
public function add(string $path_segment): PathInterface
{
$this->path []= $path_segment;
return $this;
}
public function build(): string
{
return implode(DIRECTORY_SEPARATOR, $this->path);
}
public static function fromArray(array $path): string
{
$output = new Path();
$output->compose(array_shift($path));
foreach ($path as $p) {
$output->add($p);
}
return $output->build();
}
public static function isAbsolute(string $path): bool
{
return !file_exists(Path::fromArray([$path, '..']));
}
public static function isRelative(string $path): bool
{
return !Path::isAbsolute($path);
}
public function offsetExists(mixed $offset): bool
{
return isset($this->path[$offset]);
}
public function offsetGet(mixed $offset): mixed
{
return $this->path[$offset];
}
public function offsetSet(mixed $offset, mixed $value): void
{
$this->path[$offset] = $value;
}
public function offsetUnset(mixed $offset): void
{
unset($this->path[$offset]);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Psr\Collection;
use Iterator, ArrayAccess, Countable;
interface CollectionInterface extends Iterator, ArrayAccess, Countable
{
public function set(mixed $name, $value): CollectionInterface;
public function has(mixed $name): bool;
public function get(mixed $name);
public function remove(mixed $name): CollectionInterface;
public static function fromArray(array $source): CollectionInterface;
public static function fromObject(object $source): CollectionInterface;
}

View File

@ -0,0 +1,29 @@
<?php
namespace Psr\Database;
use PDO;
interface DatabaseInterface
{
public function setHost(string $host, ?int $port = null): DatabaseInterface;
public function getHost(): string;
public function getPort(): int;
public function setName(string $database_name): DatabaseInterface;
public function getName(): string;
public function setUser(string $username, string $password): DatabaseInterface;
public function getUser(): string;
public function getPassword(): string;
public function getDsn(): string;
public function needsUser(): bool;
public function connect(): DatabaseInterface;
public function getConnection(): PDO;
public function beginTransaction(): void;
public function commit(): void;
public function rollBack(): void;
public function query(string $query): array;
public function prepare(string $query): DatabaseInterface;
public function execute(array $data): array;
public function insert(array $values): void;
public function update(array $data): void;
public function delete(array $data): void;
}

View File

@ -1,14 +1,21 @@
<?php
namespace Contabilidad\Common\Controller;
namespace Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use function Safe\{json_decode, file_get_contents};
use ProVM\Alias\Controller\Json;
use ProVM\Implement\Path;
class Base {
use Json;
class Base
{
use Json;
public function __invoke(Request $request, Response $response): Response {
return $this->withJson($response, []);
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, ContainerInterface $container): ResponseInterface
{
$filename = $container->get('documentation');
$documentation = json_decode(trim(file_get_contents($filename)));
return $this->withJson($response, $documentation);
}
}

View File

@ -1,89 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Categoria;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Categoria;
class Categorias {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$categorias = $factory->find(Categoria::class)->array();
if ($categorias) {
usort($categorias, function($a, $b) {
return strcmp($a['nombre'], $b['nombre']);
});
class Categorias extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('categoria')
->setPlural('categorias')
->setRepository($container->get(Categoria::class));
}
$output = [
'categorias' => $categorias
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'categoria' => $categoria?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$categoria = Categoria::add($factory, $in);
$results []= ['categoria' => $categoria?->toArray(), 'agregado' => $categoria?->save()];
}
} else {
$categoria = Categoria::add($factory, $input);
$results []= ['categoria' => $categoria?->toArray(), 'agregado' => $categoria?->save()];
}
$output = [
'input' => $input,
'categorias' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'old' => $categoria->toArray()
];
$input = json_decode($request->getBody());
$categoria->edit($input);
$output['categoria'] = $categoria->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'categoria' => $categoria->toArray(),
'eliminado' => $categoria->delete()
];
return $this->withJson($response, $output);
}
public function cuentas(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$cuentas = null;
if ($categoria !== null) {
$cuentas = $categoria->cuentas();
if ($cuentas !== null) {
array_walk($cuentas, function(&$item) {
$item = $item->toArray();
});
}
}
$output = [
'input' => $categoria_id,
'categoria' => $categoria?->toArray(),
'cuentas' => $cuentas
];
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Common\Controller;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Coneccion;
class Conecciones extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('coneccion')
->setPlural('conecciones')
->setRepository($container->get(Coneccion::class));
}
}

View File

@ -1,124 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Cuenta;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Cuenta;
class Cuentas {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$cuentas = $factory->find(Cuenta::class)->array();
if ($cuentas) {
usort($cuentas, function($a, $b) {
$c = strcmp($a['categoria']['nombre'], $b['categoria']['nombre']);
if ($c == 0) {
return strcmp($a['nombre'], $b['nombre']);
}
return $c;
});
class Cuentas extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('cuenta')
->setPlural('cuentas')
->setRepository($container->get(Cuenta::class));
}
$output = [
'cuentas' => $cuentas
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$cuenta = Cuenta::add($factory, $in);
$results []= ['cuenta' => $cuenta?->toArray(), 'agregado' => $cuenta?->save()];
}
} else {
$cuenta = Cuenta::add($factory, $input);
$results []= ['cuenta' => $cuenta?->toArray(), 'agregado' => $cuenta?->save()];
}
$output = [
'input' => $input,
'cuentas' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'old' => $cuenta->toArray()
];
$input = json_decode($request->getBody());
$cuenta->edit($input);
$output['cuenta'] = $cuenta->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta->toArray(),
'eliminado' => $cuenta->delete()
];
return $this->withJson($response, $output);
}
public function entradas(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$entradas = null;
if ($cuenta !== null) {
$entradas = $cuenta->entradas();
if ($entradas !== null) {
array_walk($entradas, function(&$item) {
$item = $item->toArray();
});
}
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'entradas' => $entradas
];
return $this->withJson($response, $output);
}
public function transacciones(Request $request, Response $response, Factory $factory, $cuenta_id, $limit = null, $start = 0): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$transacciones = null;
if ($cuenta !== null) {
$transacciones = $cuenta->transacciones($limit, $start);
if (count($transacciones)) {
array_walk($transacciones, function(&$item) {
$item = $item->toArray();
});
}
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
public function transaccionesAmount(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$transacciones = 0;
if ($cuenta !== null) {
$transacciones = count($cuenta->transacciones());
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Common\Controller\Estado;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Estado\Coneccion;
class Conecciones extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('estado_coneccion')
->setPlural('estados_conecciones')
->setRepository($container->get(Coneccion::class));
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Common\Controller;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Moneda;
class Monedas extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('moneda');
$this->setPlural('monedas');
$this->setRepository($container->get(Moneda::class));
}
}

View File

@ -1,75 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Transaccion;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Transaccion;
class Transacciones {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$transacciones = $factory->find(Transaccion::class)->array();
if ($transacciones !== null) {
usort($transacciones, function($a, $b) {
$d = $a['fecha'] - $b['fecha'];
if ($d === 0) {
return strcmp($a['cuenta']['nombre'], $b['cuenta']['nombre']);
}
return $d;
});
class Transacciones extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('transaccion');
$this->setPlural('transacciones');
$this->setRepository($container->get(Transaccion::class));
}
$output = [
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'transaccion' => $transaccion?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$transaccion = Transaccion::add($factory, $in);
$results []= ['transaccion' => $transaccion?->toArray(), 'agregado' => $transaccion?->save()];
}
} else {
$transaccion = Transaccion::add($factory, $input);
$results []= ['transaccion' => $transaccion?->toArray(), 'agregado' => $transaccion?->save()];
}
$output = [
'input' => $input,
'transacciones' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'old' => $transaccion->toArray()
];
$input = json_decode($request->getBody());
$transaccion->edit($input);
$output['transaccion'] = $transaccion->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'transaccion' => $transaccion->toArray(),
'eliminado' => $transaccion->delete()
];
return $this->withJson($response, $output);
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use Contabilidad\Common\Service\WorkerHandler as Handler;
class Workers {
use Json;
public function register(Request $request, Response $response, Handler $handler): Response {
$post = $request->getBody();
$result = $handler->register($post);
$output = [
'input' => $post,
'result' => $result
];
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Common\Middleware;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ResponseFactoryInterface as Factory;
use Common\Service\Auth as Service;
class Auth {
protected Factory $factory;
protected Service $service;
public function __construct(Factory $factory, Service $service) {
$this->factory = $factory;
$this->service = $service;
}
public function __invoke(Request $request, Handler $handler): Response {
if ($request->getMethod() == 'OPTIONS') {
return $handler->handle($request);
}
if (!$this->service->isValid($request)) {
$response = $this->factory->createResponse(401);
$response->getBody()->write(json_encode(['message' => 'Invalid API KEY.']));
return $response
->withHeader('Content-Type', 'application/json');
}
return $handler->handle($request);
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Common\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
use Slim\App;
use Slim\Interfaces\ErrorHandlerInterface;
use function Safe\error_log;
class Error implements ErrorHandlerInterface
{
public function __construct(App $app)
{
$this->setApp($app);
}
protected App $app;
public function setApp(App $app): Error
{
$this->app = $app;
return $this;
}
public function getApp(): App
{
return $this->app;
}
public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails): ResponseInterface
{
error_log($exception);
$response = $this->getApp()->getResponseFactory()->createResponse($exception->getCode());
$output = json_encode([
'uri' => $request->getUri()->getPath(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTrace()
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$response->getBody()->write($output);
return $response->withHeader('Content-Type', 'application/json');
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace Common\Service;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use DI\NotFoundException;
class Auth {
protected string $key;
public function __construct(string $api_key)
{
$this->key = $api_key;
}
public function isValid(Request $request): bool
{
return $this->key == $this->getKey($request);
}
public function getKey(Request $request): string
{
$errors = [];
try {
return $this->getHeaderKey($request);
} catch (NotFoundExceptionInterface $e) {
$errors []= $e;
}
try {
return $this->getBodyKey($request);
} catch (NotFoundExceptionInterface $e) {
$errors []= $e;
}
try {
return $this->getQueryKey($request);
} catch (NotFoundExceptionInterface $e) {
$errors []= $e;
}
throw new NotFoundException('API Key not found.');
}
protected function getHeaderKey(Request $request): string
{
if ($request->hasHeader('Authorization')) {
return $this->getAuthKey($request->getHeader('Authorization'));
}
throw new NotFoundException('API Key not found on header');
}
protected function getBodyKey(Request $request): string
{
if (isset($request->getParsedBody()['api_key'])) {
return $request->getParsedBody()['api_key'];
}
$post = $request->getParsedBody() ?? json_decode($request->getBody());
try {
return $this->getArrayKey($post);
} catch (\Exception $e) {
throw new NotFoundException('API Key not found in body.');
}
}
protected function getQueryKey(Request $request): string
{
try {
return $this->getArrayKey($request->getQueryParams());
} catch (\Exception $e) {
throw new NotFoundException('API Key not found in query.');
}
}
protected function getAuthKey($auth)
{
if (is_array($auth)) {
$auth = $auth[0];
}
if (str_contains($auth, 'Bearer')) {
$auth = trim(str_replace('Bearer', '', $auth), ' ,');
}
return $auth;
}
protected function getArrayKey($array) {
$posible_keys = [
'API_KEY',
'api_key',
];
foreach ($posible_keys as $key) {
if (isset($array[$key])) {
return $array[$key];
}
}
return null;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Common\Service;
use function Safe\{json_encode, json_decode, file_get_contents, file_put_contents};
use ProVM\Concept\API\Route\Route;
use ProVM\Implement\API\Route\Route as RouteObj;
class Documenter
{
public function __construct(string $filename)
{
$this->setFilename($filename);
}
protected string $filename;
public function setFilename(string $filename): Documenter
{
$this->filename = $filename;
return $this;
}
public function getFilename(): string
{
return $this->filename;
}
protected array $routes;
public function setRoutes(array $routes): Documenter
{
foreach ($routes as $path => $route) {
$this->addRoute($path, $route);
}
return $this;
}
public function addRoute(string $path, Route $route): Documenter
{
$this->routes[$path] = $route;
return $this;
}
public function getRoutes(): array
{
return $this->routes;
}
protected function load(): mixed
{
$json = json_decode(trim(file_get_contents($this->getFilename())), JSON_OBJECT_AS_ARRAY);
foreach ($json['paths'] as $path => $data) {
$route = RouteObj::add($data);
$this->addRoute($path, $route);
}
return $json;
}
public function save(): void
{
$json = $this->load();
$json['paths'] = $this->getRoutes();
ksort($json['paths']);
file_put_contents($this->getFilename(), json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
}

View File

@ -0,0 +1,179 @@
<?php
namespace Common\Service;
use Slim\App;
use ProVM\Concept\API\Controller;
use ProVM\Implement\API\Route\Route;
class Router
{
public function __construct(App &$app, Documenter $documenter)
{
$this->setApp($app);
$this->setDocumenter($documenter);
}
protected App $app;
public function setApp(App &$app): Router
{
$this->app = $app;
return $this;
}
public function getApp(): App
{
return $this->app;
}
protected Documenter $documenter;
public function setDocumenter(Documenter $documenter): Router
{
$this->documenter = $documenter;
return $this;
}
public function getDocumenter(): Documenter
{
return $this->documenter;
}
protected Controller $controller;
public function setController(Controller $controller): Router
{
$this->controller = $controller;
return $this;
}
public function getController(): Controller
{
return $this->controller;
}
protected function document(): void
{
$docs = [
"/{$this->getController()->getPlural()}" => [
'get' => [
'description' => "Entrega una lista de {$this->getController()->getPlural()}",
'responses' => [
'200' => [
'description' => "Lista de {$this->getController()->getPlural()}"
]
]
]
],
"/{$this->getController()->getPlural()}/add" => [
'post' => [
'description' => "Agregar {$this->getController()->getPlural()} con una lista",
'parameters' => [
[
'name' => $this->getController()->getPlural(),
'in' => 'query',
'description' => 'Lista de datos para agregar',
'required' => true
]
],
'responses' => [
'200' => [
'description' => "Entrega un listado de {$this->getController()->getPlural()} y si fueron agregadas"
]
]
]
],
"/{$this->getController()->getPlural()}/edit" => [
'put' => [
'description' => "Editar multiples {$this->getController()->getPlural()} con una lista",
'parameters' => [
[
'name' => $this->getController()->getPlural(),
'in' => 'query',
'description' => 'Lista de datos para editar',
'required' => true
]
],
'responses' => [
'200' => [
'description' => "Entrega un listado de {$this->getController()->getPlural()} identificando si fueron editados"
]
]
]
],
"/{$this->getController()->getSingular()}/{model_id}" => [
'get' => [
'description' => "Entrega {$this->getController()->getSingular()}",
'parameters' => [
[
'name' => 'id',
'in' => 'path',
'description' => "{$this->getController()->getSingular()} id",
'required' => true
]
],
'responses' => [
'200' => [
'description' => "{$this->getController()->getSingular()} found or null"
]
]
]
],
"/{$this->getController()->getSingular()}/{model_id}/edit" => [
'put' => [
'description' => '',
'parameters' => [
[
'name' => 'model_id',
'in' => 'path',
'description' => "{$this->getController()->getSingular()} id",
'required' => true
],
[
'name' => "{$this->getController()->getSingular()}",
'in' => 'query',
'description' => '',
'required' => true
]
],
'responses' => [
'200' => [
'description' => ''
]
]
]
],
"/{$this->getController()->getSingular()}/{model_id}/remove" => [
'delete' => [
'description' => '',
'parameters' => [
[
'name' => 'model_id',
'in' => 'path',
'description' => "{$this->getController()->getSingular()} id",
'required' => true
]
],
'responses' => [
'200' => [
'description' => ''
]
]
]
]
];
foreach ($docs as $path => $r) {
$route = Route::add($r);
$this->getDocumenter()->addRoute($path, $route);
}
$this->getDocumenter()->save();
}
public function build(): void
{
$this->getApp()->group('/' . $this->getController()->getPlural(), function($app) {
$app->post('/add[/]', [get_class($this->getController()), 'add']);
$app->put('/edit[/]', [get_class($this->getController()), 'edit']);
$app->get('[/]', get_class($this->getController()));
});
$this->getApp()->group('/' . $this->getController()->getSingular() . '/{model_id}', function($app) {
$app->put('/edit[/]', [get_class($this->getController()), 'editOne']);
$app->delete('/remove[/]', [get_class($this->getController()), 'remove']);
$app->get('[/]', [get_class($this->getController()), 'get']);
});
$this->document();
}
}

View File

@ -7,10 +7,12 @@
"php-di/slim-bridge": "^3.1",
"nyholm/psr7": "^1.4",
"nyholm/psr7-server": "^1.0",
"nesbot/carbon": "^2.50",
"robmorgan/phinx": "^0.12.9",
"odan/phinx-migrations-generator": "^5.4",
"martin-mikac/csv-to-phinx-seeder": "^1.6",
"zeuxisoo/slim-whoops": "^0.7.3",
"provm/controller": "^1.0",
"provm/models": "^1.0.0-rc3",
"nesbot/carbon": "^2.50"
"thecodingmachine/safe": "^2.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
@ -24,20 +26,12 @@
],
"autoload": {
"psr-4": {
"Contabilidad\\Common\\": "common",
"Contabilidad\\": "src"
"Common\\": "common/",
"Contabilidad\\": "src/",
"ProVM\\": "ProVM/",
"Psr\\": "Psr/"
}
},
"repositories": [
{
"type": "git",
"url": "http://git.provm.cl/ProVM/controller.git"
},
{
"type": "git",
"url": "http://git.provm.cl/ProVM/models.git"
}
],
"config": {
"secure-http": false
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoCategoria extends AbstractMigration
{
public function change(): void
{
$this->table('tipos_categorias')
->addColumn('nombre', 'string')
->create();
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoEstadoConeccion extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('tipos_estados_conecciones')
->addColumn('descripcion', 'string')
->create();
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Categoria extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('categorias')
->addColumn('nombre', 'string')
->addColumn('tipo_id', 'integer')
->create();
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Coneccion extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('conecciones')
->addColumn('key', 'string')
->create();
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoTransaccion extends AbstractMigration
{
public function change(): void
{
$this->table('tipos_transacciones')
->addColumn('nombre', 'string')
->addColumn('color', 'string')
->create();
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Cuenta extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('cuentas')
->addColumn('nombre', 'string')
->create();
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class EstadoConeccion extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('estados_conecciones')
->addColumn('coneccion_id', 'integer')
->addColumn('fecha', 'date')
->addColumn('tipo_id', 'integer')
->create();
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Transaccion extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('transacciones')
->addColumn('cuenta_id', 'integer')
->addColumn('glosa', 'string')
->addColumn('tipo_id', 'integer')
->addColumn('valor', 'double')
->addColumn('categoria_id', 'integer')
->addColumn('fecha', 'datetime')
->create();
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Moneda extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('monedas')
->addColumn('denominacion', 'string')
->addColumn('codigo', 'string', ['length' => 3])
->addColumn('prefijo', 'string', ['default' => ''])
->addColumn('sufijo', 'string', ['default' => ''])
->addColumn('decimales', 'integer', ['default' => 0])
->create();
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CuentaMoneda extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('cuentas')
->addColumn('moneda_id', 'integer')
->update();
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoCambio extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('tipos_cambio')
->addColumn('fecha', 'datetime')
->addColumn('desde_id', 'integer')
->addForeignKey('desde_id', 'monedas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('hasta_id', 'integer')
->addForeignKey('hasta_id', 'monedas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('valor', 'double')
->create();
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CategoriaForeign extends AbstractMigration
{
public function change(): void
{
$this->table('categorias')
->addForeignKey('tipo_id', 'tipos_categorias', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class EstadoConeccionForeign extends AbstractMigration
{
public function change(): void
{
$this->table('estados_conecciones')
->addForeignKey('coneccion_id', 'conecciones', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('tipo_id', 'tipos_estados_conecciones', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TransaccionForeign extends AbstractMigration
{
public function change(): void
{
$this->table('transacciones')
->addForeignKey('cuenta_id', 'cuentas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('tipo_id', 'tipos_transacciones', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('categoria_id', 'categorias', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CuentaMonedaForeign extends AbstractMigration
{
public function change(): void
{
$this->table('cuentas')
->addForeignKey('moneda_id', 'monedas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

41
api/db/seeds/Moneda.php Normal file
View File

@ -0,0 +1,41 @@
<?php
use Phinx\Seed\AbstractSeed;
class Moneda extends AbstractSeed
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* https://book.cakephp.org/phinx/0/en/seeding.html
*/
public function run()
{
$data = [
[
'denominacion' => 'Pesos Chilenos',
'codigo' => 'CLP',
'prefijo' => '$ '
],
[
'denominacion' => 'Dólar',
'codigo' => 'USD',
'prefijo' => 'US$ ',
'decimales' => 2
],
[
'denominacion' => 'Unidad de Fomento',
'codigo' => 'CLF',
'sufijo' => ' UF',
'decimales' => 2
]
];
$this->table('monedas')
->insert($data)
->saveData();
}
}

View File

@ -0,0 +1,30 @@
<?php
use Phinx\Seed\AbstractSeed;
class TipoEstadoConeccion extends AbstractSeed
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* https://book.cakephp.org/phinx/0/en/seeding.html
*/
public function run()
{
$data = [
[
'descripcion' => 'Activa'
],
[
'descripcion' => 'Inactiva'
]
];
$this->table('tipos_estado_coneccion')
->insert($data)
->saveData();
}
}

View File

@ -5,25 +5,26 @@ server {
access_log /var/log/nginx/access.log;
root /app/public;
client_max_body_size 50M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,
X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'application/json';
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,
X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
try_files $uri =404;

41
api/phinx.php Normal file
View File

@ -0,0 +1,41 @@
<?php
return
[
'paths' => [
'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds'
],
'environments' => [
'default_migration_table' => 'phinxlog',
'default_environment' => 'development',
'production' => [
'adapter' => 'mysql',
'host' => 'db',
'name' => $_ENV['MYSQL_DATABASE'],
'user' => $_ENV['MYSQL_USER'],
'pass' => $_ENV['MYSQL_PASSWORD'],
'port' => '3306',
'charset' => 'utf8',
],
'development' => [
'adapter' => 'mysql',
'host' => 'db',
'name' => $_ENV['MYSQL_DATABASE'],
'user' => $_ENV['MYSQL_USER'],
'pass' => $_ENV['MYSQL_PASSWORD'],
'port' => '3306',
'charset' => 'utf8',
],
'testing' => [
'adapter' => 'mysql',
'host' => 'db',
'name' => $_ENV['MYSQL_DATABASE'],
'user' => $_ENV['MYSQL_USER'],
'pass' => $_ENV['MYSQL_PASSWORD'],
'port' => '3306',
'charset' => 'utf8',
]
],
'version_order' => 'creation'
];

4
api/php.ini Normal file
View File

@ -0,0 +1,4 @@
log_errors = true
error_log = /var/log/php/error.log
upload_max_filesize = 50M
max_input_vars = 5000

View File

@ -1,5 +1,6 @@
<?php
require_once implode(DIRECTORY_SEPARATOR, [
ini_set('error_reporting', E_ALL & ~E_NOTICE & ~E_DEPRECATED);
$app = require_once implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__),
'setup',
'app.php'

View File

@ -0,0 +1,433 @@
{
"openapi": "3.0.0",
"info": {
"title": "Contabilidad",
"version": "1.0.0"
},
"paths": {
"/coneccion/{model_id}": {
"get": {
"description": "Entrega coneccion",
"parameters": [
{
"name": "id",
"in": "path",
"description": "coneccion id",
"required": true
}
],
"responses": {
"200": {
"description": "coneccion found or null"
}
}
}
},
"/coneccion/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "coneccion id",
"required": true
},
{
"name": "coneccion",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/coneccion/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "coneccion id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/conecciones": {
"get": {
"description": "Entrega una lista de conecciones",
"responses": {
"200": {
"description": "Lista de conecciones"
}
}
}
},
"/conecciones/add": {
"post": {
"description": "Agregar conecciones con una lista",
"parameters": [
{
"name": "conecciones",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de conecciones y si fueron agregadas"
}
}
}
},
"/conecciones/edit": {
"put": {
"description": "Editar multiples conecciones con una lista",
"parameters": [
{
"name": "conecciones",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de conecciones identificando si fueron editados"
}
}
}
},
"/cuenta/{model_id}": {
"get": {
"description": "Entrega cuenta",
"parameters": [
{
"name": "id",
"in": "path",
"description": "cuenta id",
"required": true
}
],
"responses": {
"200": {
"description": "cuenta found or null"
}
}
}
},
"/cuenta/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "cuenta id",
"required": true
},
{
"name": "cuenta",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/cuenta/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "cuenta id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/cuentas": {
"get": {
"description": "Entrega una lista de cuentas",
"responses": {
"200": {
"description": "Lista de cuentas"
}
}
}
},
"/cuentas/add": {
"post": {
"description": "Agregar cuentas con una lista",
"parameters": [
{
"name": "cuentas",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de cuentas y si fueron agregadas"
}
}
}
},
"/cuentas/edit": {
"put": {
"description": "Editar multiples cuentas con una lista",
"parameters": [
{
"name": "cuentas",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de cuentas identificando si fueron editados"
}
}
}
},
"/moneda/{model_id}": {
"get": {
"description": "Entrega moneda",
"parameters": [
{
"name": "id",
"in": "path",
"description": "moneda id",
"required": true
}
],
"responses": {
"200": {
"description": "moneda found or null"
}
}
}
},
"/moneda/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "moneda id",
"required": true
},
{
"name": "moneda",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/moneda/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "moneda id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/monedas": {
"get": {
"description": "Entrega una lista de monedas",
"responses": {
"200": {
"description": "Lista de monedas"
}
}
}
},
"/monedas/add": {
"post": {
"description": "Agregar monedas con una lista",
"parameters": [
{
"name": "monedas",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de monedas y si fueron agregadas"
}
}
}
},
"/monedas/edit": {
"put": {
"description": "Editar multiples monedas con una lista",
"parameters": [
{
"name": "monedas",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de monedas identificando si fueron editados"
}
}
}
},
"/transaccion/{model_id}": {
"get": {
"description": "Entrega transaccion",
"parameters": [
{
"name": "id",
"in": "path",
"description": "transaccion id",
"required": true
}
],
"responses": {
"200": {
"description": "transaccion found or null"
}
}
}
},
"/transaccion/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "transaccion id",
"required": true
},
{
"name": "transaccion",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/transaccion/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "transaccion id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/transacciones": {
"get": {
"description": "Entrega una lista de transacciones",
"responses": {
"200": {
"description": "Lista de transacciones"
}
}
}
},
"/transacciones/add": {
"post": {
"description": "Agregar transacciones con una lista",
"parameters": [
{
"name": "transacciones",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de transacciones y si fueron agregadas"
}
}
}
},
"/transacciones/edit": {
"put": {
"description": "Editar multiples transacciones con una lista",
"parameters": [
{
"name": "transacciones",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de transacciones identificando si fueron editados"
}
}
}
}
}
}

View File

@ -1,4 +1,4 @@
<?php
use Contabilidad\Common\Controller\Base;
use Common\Controller\Base;
$app->get('/', Base::class);

View File

@ -1,13 +1,6 @@
<?php
use Contabilidad\Common\Controller\Categorias;
use Common\Controller\Categorias;
$app->group('/categorias', function($app) {
$app->post('/add[/]', [Categorias::class, 'add']);
$app->get('[/]', Categorias::class);
});
$app->group('/categoria/{categoria_id}', function($app) {
$app->get('/cuentas', [Categorias::class, 'cuentas']);
$app->put('/edit', [Categorias::class, 'edit']);
$app->delete('/delete', [Categorias::class, 'delete']);
$app->get('[/]', [Categorias::class, 'show']);
});
$router = $app->getContainer()->get(\Common\Service\Router::class);
$router->setController($app->getContainer()->get(Categorias::class));
$router->build();

View File

@ -0,0 +1,6 @@
<?php
use Common\Controller\Conecciones;
$router = $app->getContainer()->get(\Common\Service\Router::class);
$router->setController($app->getContainer()->get(Conecciones::class));
$router->build();

View File

@ -1,17 +1,6 @@
<?php
use Contabilidad\Common\Controller\Cuentas;
use Common\Controller\Cuentas;
$app->group('/cuentas', function($app) {
$app->post('/add[/]', [Cuentas::class, 'add']);
$app->get('[/]', Cuentas::class);
});
$app->group('/cuenta/{cuenta_id}', function($app) {
$app->get('/entradas', [Cuentas::class, 'entradas']);
$app->group('/transacciones', function($app) {
$app->get('/amount', [Cuentas::class, 'transaccionesAmount']);
$app->get('[/{limit:[0-9]+}[/{start:[0-9]+}]]', [Cuentas::class, 'transacciones']);
});
$app->put('/edit', [Cuentas::class, 'edit']);
$app->delete('/delete', [Cuentas::class, 'delete']);
$app->get('[/]', [Cuentas::class, 'show']);
});
$router = $app->getContainer()->get(\Common\Service\Router::class);
$router->setController($app->getContainer()->get(Cuentas::class));
$router->build();

View File

@ -0,0 +1,6 @@
<?php
use Common\Controller\Monedas;
$router = $app->getContainer()->get(\Common\Service\Router::class);
$router->setController($app->getContainer()->get(Monedas::class));
$router->build();

View File

@ -1,12 +1,6 @@
<?php
use Contabilidad\Common\Controller\Transacciones;
use Common\Controller\Transacciones;
$app->group('/transacciones', function($app) {
$app->post('/add[/]', [Transacciones::class, 'add']);
$app->get('[/]', Transacciones::class);
});
$app->group('/transaccion/{transaccion_id}', function($app) {
$app->put('/edit', [Transacciones::class, 'edit']);
$app->delete('/delete', [Transacciones::class, 'delete']);
$app->get('[/]', [Transacciones::class, 'show']);
});
$router = $app->getContainer()->get(\Common\Service\Router::class);
$router->setController($app->getContainer()->get(Transacciones::class));
$router->build();

View File

@ -1,6 +0,0 @@
<?php
use Contabilidad\Common\Controller\Workers;
$app->group('/workers', function($app) {
$app->post('/register', [Workers::class, 'register']);
});

View File

@ -1,7 +1,6 @@
<?php
use DI\ContainerBuilder as Builder;
use DI\Bridge\Slim\Bridge as Bridge;
use Zeuxisoo\Whoops\Slim\WhoopsMiddleware;
include_once 'composer.php';
@ -24,14 +23,9 @@ foreach ($folders as $f) {
}
}
$container = $builder->build();
$app = Bridge::create($container);
$app = Bridge::create($builder->build());
$app->addRoutingMiddleware();
$app->add(new WhoopsMiddleware());
$folder = 'middlewares';
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'middlewares']);
if (file_exists($folder)) {
$files = new DirectoryIterator($folder);
foreach ($files as $file) {
@ -42,5 +36,4 @@ if (file_exists($folder)) {
}
}
include_once 'databases.php';
include_once 'router.php';
return $app;

View File

@ -1,37 +0,0 @@
<?php
$databases = $app->getContainer()->get('databases');
foreach ($databases->databases as $name => $settings) {
if (!is_object($settings)) {
continue;
}
$auth = false;
$dsn = '';
switch (strtolower($settings->type)) {
case 'mysql':
$data = [
['host', $settings->host->name],
['dbname', $settings->name]
];
if (isset($settings->host->port)) {
$data []= ['port', $settings->host->port];
}
array_walk($data, function(&$item) {
$item = implode('=', $item);
});
$dsn = implode(':', [
'mysql',
implode(';', $data)
]);
$auth = true;
break;
}
ORM::configure($dsn, null, $name);
if ($auth) {
ORM::configure('username', $settings->user->name, $name);
ORM::configure('password', $settings->user->password, $name);
}
}
if (isset($databases->short_names) and $databases->short_names) {
Model::$short_table_names = true;
}

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