First fully working version of saving snips content with git control

This commit is contained in:
Tim 2023-04-04 23:23:53 +02:00
parent 1a7c9bb25a
commit 842c936d8c
12 changed files with 170 additions and 17 deletions

View File

@ -7,6 +7,7 @@
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"czproject/git-php": "^4.1",
"doctrine/doctrine-bundle": "^2.9",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.14",

54
composer.lock generated
View File

@ -4,8 +4,60 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5cd09d345b4ecefaece6f03c919d953e",
"content-hash": "975714eae301e2eb9cd72cfce038b337",
"packages": [
{
"name": "czproject/git-php",
"version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/czproject/git-php.git",
"reference": "bb195e30442bc5206b30fa9a304e20ea8e96458f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/czproject/git-php/zipball/bb195e30442bc5206b30fa9a304e20ea8e96458f",
"reference": "bb195e30442bc5206b30fa9a304e20ea8e96458f",
"shasum": ""
},
"require": {
"php": ">=5.6.0"
},
"require-dev": {
"nette/tester": "^2.0"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jan Pecha",
"email": "janpecha@email.cz"
}
],
"description": "Library for work with Git repository in PHP.",
"keywords": [
"git"
],
"support": {
"issues": "https://github.com/czproject/git-php/issues",
"source": "https://github.com/czproject/git-php/tree/v4.1.0"
},
"funding": [
{
"url": "https://www.paypal.com/donate?hosted_button_id=EPSKR7MGVV7KC",
"type": "custom"
}
],
"time": "2022-12-10T18:23:32+00:00"
},
{
"name": "doctrine/cache",
"version": "2.2.0",

View File

@ -23,3 +23,7 @@ services:
App\Service\LastRelease:
arguments:
- '%kernel.project_dir%/release.json'
App\Service\SnipServiceFactory:
arguments:
- '%kernel.project_dir%/var/snips'

View File

@ -5,6 +5,7 @@ namespace App\Controller;
use App\Entity\Snip;
use App\Form\SnipType;
use App\Repository\SnipRepository;
use App\Service\SnipServiceFactory;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
@ -16,6 +17,7 @@ class SnipController extends AbstractController
{
public function __construct(
private readonly SnipRepository $repository,
private readonly SnipServiceFactory $snipServiceFactory,
)
{
}
@ -24,22 +26,26 @@ class SnipController extends AbstractController
public function index(): Response
{
return $this->render('snip/index.html.twig', [
'snips' => $this->repository->findAll(),
'snips' => $this->repository->findByUser($this->getUser()),
]);
}
#[Route('/singe/{snip}', name: '_single')]
#[Route('/single/{snip}', name: '_single')]
public function single(Snip $snip): Response
{
return $this->render('snip/single.html.twig', [
'snip' => $snip,
'content' => $this->snipServiceFactory->create($snip)->get(),
]);
}
#[Route('/edit/{snip}', name: '_edit')]
public function edit(Snip $snip, Request $request): Response
{
$snipService = $this->snipServiceFactory->create($snip);
$form = $this->createForm(SnipType::class, $snip);
$form->get('content')->setData($snipService->get());
$form->add('Save', SubmitType::class);
$form->handleRequest($request);
@ -47,6 +53,7 @@ class SnipController extends AbstractController
$snip->setCreatedAtTodayNoSeconds()
->setCreatedBy($this->getUser());
$this->repository->save($snip);
$snipService->update($form->get('content')->getData());
$this->addFlash('success', sprintf('Snip "%s" saved successfully', $snip));

View File

@ -40,11 +40,6 @@ trait TrackedTrait
return $this;
}
public function getCode(): string
{
return date_format($this->createdAt, "ymd");
}
public function setCreatedAtTodayNoSeconds(): self
{
$this->setCreatedAt(DateTime::createFromFormat('Y-m-d H:i', date('Y-m-d H:i')));

View File

@ -3,6 +3,7 @@
namespace App\Repository;
use App\Entity\Snip;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
@ -38,4 +39,14 @@ class SnipRepository extends ServiceEntityRepository
$this->getEntityManager()->flush();
}
}
public function findByUser(User $user): array
{
$qb = $this->createQueryBuilder('s');
$qb->where('s.createdBy = :user')
->setParameter('user', $user)
->orderBy('s.createdAt', 'DESC');
return $qb->getQuery()->getResult();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Service;
use App\Entity\Snip;
use App\Entity\User;
use CzProject\GitPhp\Git;
use CzProject\GitPhp\GitRepository;
class SnipService
{
private GitRepository $repo;
public function __construct(
Snip $snip,
string $snipBasePath,
private readonly User $user,
)
{
$git = new Git();
$repoPath = sprintf('%s/%s', $snipBasePath, $snip->getId());
if (!is_dir($repoPath)) {
$this->repo = $git->init($repoPath);
} else {
$this->repo = $git->open($repoPath);
}
}
private function snipExists(): bool
{
return file_exists($this->getSnipPath());
}
private function getSnipPath(): string
{
return sprintf('%s/snip.txt', $this->repo->getRepositoryPath());
}
public function update(string $snipContents): void
{
file_put_contents($this->getSnipPath(), $snipContents);
$this->repo->addFile('snip.txt');
if ($this->repo->hasChanges()) {
$this->repo->commit(sprintf('Updated snip at %s by %s', date('Y-m-d H:i:s'), $this->user));
}
}
public function get(): string
{
if (!$this->snipExists()) {
return '';
}
return file_get_contents($this->getSnipPath());
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Service;
use App\Entity\Snip;
use Symfony\Bundle\SecurityBundle\Security;
class SnipServiceFactory
{
public function __construct(
private readonly string $snipBasePath,
private readonly Security $security,
)
{
}
public function create(Snip $snip): SnipService
{
return new SnipService($snip, $this->snipBasePath, $this->security->getUser());
}
}

View File

@ -3,13 +3,8 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}BLES{% endblock %}</title>
<title>{% block title %}SNIPS{% endblock %}</title>
<link rel="shortcut icon" type="image/jpg" href="/favicon.png">
<script src="https://kit.fontawesome.com/3471b6556e.js" crossorigin="anonymous"></script>
{% if chartjs|default(false) %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@2.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
{% endif %}
{% block css %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
@ -59,6 +54,7 @@
{# javascript block #}
{% block js %}
<script src="https://kit.fontawesome.com/3471b6556e.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
crossorigin="anonymous"></script>

View File

@ -1,6 +1,6 @@
{% extends 'base/base.html.twig' %}
{% block body %}
<h2>{{ snip }}</h2>
<h2>Editing {{ snip }}</h2>
{{ form(form) }}
{% endblock %}

View File

@ -5,7 +5,7 @@
<a class="btn btn-primary" href="{{ path('snip_new') }}">Create a new Snip</a>
<div class="list-group">
{% for snip in snips %}
<a class="list-group-item" href="{{ path('snip_edit', {snip: snip.id}) }}">
<a class="list-group-item" href="{{ path('snip_single', {snip: snip.id}) }}">
{{ snip }}
</a>
{% endfor %}

View File

@ -1,5 +1,15 @@
{% extends 'base/base.html.twig' %}
{% block body %}
<h1>{{ snip }}</h1>
<div class="card" style="width: 100%;">
<h4 class="card-header">
<a class="btn btn-sm btn-outline-info" href="{{ path('snip_edit', {snip: snip.id}) }}">
<i class="fa fa-pencil" aria-hidden="true"></i>
</a>
{{ snip }}
</h4>
<div class="card-body">
<p class="card-text">{{ content }}</p>
</div>
</div>
{% endblock %}