Skip to content
Snippets Groups Projects
Commit e3a36b87 authored by Laurent Lecluse's avatar Laurent Lecluse
Browse files

Wip

parent eaaf6205
No related branches found
No related tags found
No related merge requests found
Pipeline #19019 passed
CHANGELOG
=========
6.0.0 (à venir)
------------------
<?php
namespace UnicaenVue;
use Laminas\ModuleManager\Feature\ConfigProviderInterface;
use Laminas\Mvc\MvcEvent;
/**
*
*
* @author Laurent LECLUSE <laurent.lecluse at unicaen.fr>
*/
class Module implements ConfigProviderInterface
{
public function onBootstrap(MvcEvent $e)
{
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
# vue
# Unicaen/Vue
L'objectif de ce module est de proposer des outils pour utiliser Vue.js avec quelques outils connexes
et intégrer le tout à l'écosystème Unicaen.
- [Liste des changements](CHANGELOG.md)
- [Installation](doc/install.md)
- [Documentation](doc/doc.md)
## Getting started
## Prérequis
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
- PHP 8
- Bootstrap 5
- Node.js (sur les machines de développement uniquement : pas en production)
- Laminas
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
## Outils embarqués
```
cd existing_repo
git remote add origin https://git.unicaen.fr/lib/unicaen/vue.git
git branch -M master
git push -uf origin master
```
### [Vue.js](https://vuejs.org/)
## Integrate with your tools
Version 3
- [ ] [Set up project integrations](https://git.unicaen.fr/lib/unicaen/vue/-/settings/integrations)
Vue est un framework JavaScript pour la construction d'interfaces utilisateur.
Il s'appuie sur HTML, CSS et JavaScript standards et fournit un modèle de programmation déclaratif
et basé sur les composants qui vous aide à développer efficacement des interfaces utilisateur,
qu'elles soient simples ou complexes.
## Collaborate with your team
### [Vite](https://vitejs.dev)
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
Version 4
## Test and Deploy
Vite est un outil de construction qui vise à fournir une expérience de développement plus rapide et plus épurée
pour les projets web modernes. Il se compose de deux parties majeures :
Use the built-in continuous integration in GitLab.
- Un serveur de développement qui offre des améliorations de fonctionnalités riches par rapport aux modules ES
natifs, par exemple le remplacement de module à chaud (HMR ou hot-loading) extrêmement rapide.
- Une commande de construction qui regroupe votre code avec Rollup, pré-configuré pour produire des ressources
statiques hautement optimisées pour la production.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
Vite est extensible via son API de plugin et son API JavaScript avec un support de typage complet.
***
### [BootstrapVue](https://bootstrap-vue.org/)
# Editing this README
Version Next (l'officielle n'est pas encore compatible Vue3/BS5)
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
Avec BootstrapVue, vous pouvez construire des projets web réactifs, axés sur les mobiles et accessibles ARIA
en utilisant des composants Vue.js pour générer du HTML compatible Bootstrap
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
### [Axios](https://axios-http.com/)
## Name
Choose a self-explaining name for your project.
Version 1
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
Axios est un client HTTP basé sur les promesses.
Côté serveur, un plugin de contrôleur est proposé ainsi qu'un système permettant de construire des arborescences JSON
et côté client (navigateur) il utilise les XMLHttpRequests.
<?php
// Generated by UnicaenCode
return [
];
\ No newline at end of file
{
"name" : "unicaen/vue",
"description" : "Module d'intégration de Vue.js avec l'écosystème Unicaen",
"description" : "Module pour utiliser Vue.js",
"repositories": [
{
"type": "composer",
......
<?php
namespace UnicaenVue;
use UnicaenVue\Controller\Plugin\Axios;
use UnicaenVue\Controller\Plugin\AxiosFactory;
use UnicaenVue\View\Helper\ViteViewHelper;
use UnicaenVue\View\Helper\VueViewHelper;
return [
'controller_plugins' => [
'alias' => [
'axios' => Axios::class,
],
'factories' => [
Axios::class => AxiosFactory::class,
],
],
'service_manager' => [
'factories' => [
],
],
'view_helpers' => [
'aliases' => [
'vite' => ViteViewHelper::class,
'vue' => VueViewHelper::class,
],
'factories' => [
ViteViewHelper::class => View\Helper\ViteViewHelperFactory::class,
VueViewHelper::class => View\Helper\VueViewHelperFactory::class,
],
],
];
\ No newline at end of file
<?php
return [
/* Configuration d'UnicaenVue */
'unicaen-vue' => [
],
];
\ No newline at end of file
# Documentation du module Unicaen/Vue
<?php
namespace UnicaenVue\Controller\Plugin;
use Application\Constants;
use Application\Interfaces\AxiosExtractor;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Query;
use Laminas\Mvc\Controller\Plugin\AbstractPlugin;
use Laminas\Mvc\Plugin\FlashMessenger\FlashMessenger;
use Laminas\View\Model\JsonModel;
/**
* Description of Axios
*
* @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
*/
class Axios extends AbstractPlugin
{
const DATETIME_FORMAT = 'Y-m-d\TH:i:s.u\Z'; // timestamp ISO 8601 pour HTML5
public function fromPost(?string $param = null, $default = null)
{
$post = (array)json_decode(file_get_contents('php://input'));
if ($param) {
if (array_key_exists($param, $post)) {
return $post[$param];
} else {
return $default;
}
} else {
return $post;
}
}
public function send($data, array $properties = []): JsonModel
{
$data = self::extract($data, $properties);
/** @var FlashMessenger $flashMessenger */
$flashMessenger = $this->controller->flashMessenger();
$namespaces = [
$flashMessenger::NAMESPACE_SUCCESS,
$flashMessenger::NAMESPACE_WARNING,
$flashMessenger::NAMESPACE_ERROR,
$flashMessenger::NAMESPACE_INFO,
];
$messages = [];
foreach ($namespaces as $namespace) {
if ($flashMessenger->hasCurrentMessages($namespace)) {
$messages[$namespace] = $flashMessenger->getCurrentMessages($namespace);
$flashMessenger->clearCurrentMessages($namespace);
}
}
$jsonData = [
'data' => $data,
'messages' => $messages,
];
$model = new JsonModel($jsonData);
return $model;
}
public static function extract($data, array $properties = [])
{
if ($data instanceof Query) {
return self::extract($data->getResult(), $properties);
} elseif (self::isList($data)) {
return self::extractList($data, $properties);
} elseif (is_array($data)) {
return self::extractArray($data, $properties);
} elseif ($data instanceof \DateTime) {
return $data->format(self::DATETIME_FORMAT);
} elseif (is_object($data)) {
return self::extractObject($data, $properties);
} else {
return $data;
}
}
protected static function extractObject($data, array $properties): array
{
$result = [];
$props = ['id'];
if (empty($properties)) {
if ($data instanceof AxiosExtractor) {
$ad = $data->axiosDefinition();
foreach ($ad as $prop) {
if ($prop !== 'id') {
$props[] = $prop;
}
}
} else {
if (method_exists($data, '__toString')) {
$props[] = '__toString';
}
}
} else {
foreach ($properties as $prop) {
if ($prop !== 'id') {
$props[] = $prop;
}
}
}
foreach ($props as $property) {
if (is_array($property)) {
$subProperties = $property[1];
$property = $property[0];
} else {
$subProperties = [];
}
$methods = [
$property,
'get' . ucfirst($property),
'is' . ucfirst($property),
'has' . ucfirst($property),
];
foreach ($methods as $method) {
if (method_exists($data, $method)) {
$value = $data->$method();
$result[$property] = self::extract($value, $subProperties);
break;
}
}
}
if (array_key_exists('__toString', $result)) {
$result['libelle'] = $result['__toString'];
unset($result['__toString']);
}
return $result;
}
protected static function extractArray(array $data, array $properties): array
{
$result = [];
$props = ['id'];
if (empty($properties)) {
$properties = array_keys($data);
}
foreach ($properties as $prop) {
if ($prop !== 'id') {
$props[] = $prop;
}
}
foreach ($props as $property) {
if (is_array($property)) {
$subProperties = $property[1];
$property = $property[0];
} else {
$subProperties = [];
}
if (array_key_exists($property, $data)) {
$result[$property] = self::extract($data[$property], $subProperties);
}
}
return $result;
}
protected static function extractList($list, array $properties = []): array
{
$result = [];
foreach ($list as $sobj) {
$result[] = self::extract($sobj, $properties);
}
return $result;
}
protected static function isList($data): bool
{
if ($data instanceof Collection) {
return true;
}
if (!is_array($data)) {
return false;
}
foreach ($data as $k => $v) {
if (!is_numeric($k)) {
// une clé non numérique est rejetée
return false;
}
if (!(is_array($v) || is_object($v))) {
// une liste doit être une liste d'objets ou bien une liste de tableaux
return false;
}
}
return true;
}
}
\ No newline at end of file
<?php
namespace UnicaenVue\Controller\Plugin;
use Psr\Container\ContainerInterface;
/**
* Description of AxiosFactory
*
* @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
*/
class AxiosFactory
{
/**
* @param ContainerInterface $container
* @param string $requestedName
* @param array|null $options
*
* @return Axios
*/
public function __invoke(ContainerInterface $container, $requestedName, $options = null): Axios
{
$plugin = new Axios;
/* Injectez vos dépendances ICI */
return $plugin;
}
}
\ No newline at end of file
<?php
namespace UnicaenVue\View\Helper;
use Laminas\View\Helper\AbstractHtmlElement;
/**
* Description of ViteViewHelper
*
* @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
*/
class ViteViewHelper extends AbstractHtmlElement
{
private $config = [
'host' => 'http://localhost:5133',
'vue-url' => '/vendor/vue.js',
'hot-loading' => true,
];
/**
* @param string[] $config
*/
public function __construct(array $config)
{
$this->config = $config;
}
/**
*
* @return self|string
*/
public function __invoke(?string $entry = null)
{
if (!empty($entry)) {
return $this->vite($entry);
}
return $this;
}
/**
* Retourne le code HTML.
*
* @return string Code HTML
*/
public function __toString(): string
{
return $this->render();
}
/**
*
*
* @return string Code HTML
*/
public function render(): string
{
$r = '';
/* Complétez */
return $r;
}
public function useNode(): bool
{
return $this->getConfig('hot-loading');
}
public function getConfig(string $param)
{
if (array_key_exists($param, $this->config)){
return $this->config[$param];
}else{
return null;
}
}
public function setConfig(string $param, $value): self
{
$this->config[$param] = $value;
return $this;
}
public function head(): string
{
$h = '';
if ($this->useNode()) {
$url = $this->getView()->basePath($this->getConfig('vue-url'));
$h = '<script type="text/javascript" src="' . $url . '"></script>';
}
$h .= $this->vite('main.js');
return $h;
}
public function vite(string $entry): string
{
return "\n" . $this->jsTag($entry)
. "\n" . $this->jsPreloadImports($entry)
. "\n" . $this->cssTag($entry);
}
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
protected function jsTag(string $entry): string
{
$url = $this->useNode()
? $this->getConfig('host') . '/' . $entry
: $this->assetUrl($entry);
if (!$url) {
return '';
}
return '<script type="module" crossorigin src="' . $url . '"></script>';
}
protected function jsPreloadImports(string $entry): string
{
if ($this->useNode()) {
return '';
}
$res = '';
foreach ($this->importsUrls($entry) as $url) {
$res .= '<link rel="modulepreload" href="' . $url . '">';
}
return $res;
}
protected function cssTag(string $entry): string
{
// not needed on dev, it's inject by Vite
if ($this->useNode()) {
return '';
}
$tags = '';
foreach ($this->cssUrls($entry) as $url) {
$tags .= '<link rel="stylesheet" href="' . $url . '">';
}
return $tags;
}
protected function getManifest(): array
{
$content = file_get_contents(getcwd() . '/public/dist/manifest.json');
return json_decode($content, true);
}
protected function assetUrl(string $entry): string
{
$manifest = $this->getManifest();
return isset($manifest[$entry])
? $this->getView()->basePath('/dist/' . $manifest[$entry]['file'])
: '';
}
protected function importsUrls(string $entry): array
{
$urls = [];
$manifest = $this->getManifest();
if (!empty($manifest[$entry]['imports'])) {
foreach ($manifest[$entry]['imports'] as $imports) {
$urls[] = $this->getView()->basePath('/dist/' . $manifest[$imports]['file']);
}
}
return $urls;
}
protected function cssUrls(string $entry): array
{
$urls = [];
$manifest = $this->getManifest();
if (!empty($manifest[$entry]['css'])) {
foreach ($manifest[$entry]['css'] as $file) {
$urls[] = $this->getView()->basePath('/dist/' . $file);
}
}
return $urls;
}
}
\ No newline at end of file
<?php
namespace UnicaenVue\View\Helper;
use Psr\Container\ContainerInterface;
/**
* Description of ViteViewHelperFactory
*
* @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
*/
class ViteViewHelperFactory
{
/**
* @param ContainerInterface $container
* @param string $requestedName
* @param array|null $options
*
* @return ViteViewHelper
*/
public function __invoke(ContainerInterface $container, $requestedName, $options = null): ViteViewHelper
{
$config = $container->get('config');
if (array_key_exists('vite', $config)) {
$viteConfig = $config['vite'];
} else {
$viteConfig = [
'host' => 'http://localhost:5133',
'vue-url' => '/vendor/vue.js',
'hot-loading' => true,
];
}
$viewHelper = new ViteViewHelper($viteConfig);
return $viewHelper;
}
}
\ No newline at end of file
<?php
namespace UnicaenVue\View\Helper;
use Doctrine\Common\Collections\Collection;
use Laminas\View\Helper\AbstractHtmlElement;
use UnicaenVue\Controller\Plugin\Axios;
/**
* Description of ViteViewHelper
*
* @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
*/
class VueViewHelper extends AbstractHtmlElement
{
protected bool $inVue = false;
/**
*
* @return self|string
*/
public function __invoke(string $name = null, array $props = [])
{
if (!empty($name)) {
$h = $this->begin();
$h .= $this->component($name, $props);
$h .= $this->end();
return $h;
}
return $this;
}
/**
* Démarre une nouvelle zone de Vue
*
* @return string
*/
public function begin(): string
{
$this->inVue = true;
return $this->getView()->tag("div", ['class' => 'vue-app']);
}
/**
* Termine une zone de Vue
*
* @return string
*/
public function end(): string
{
$this->inVue = false;
return "</div>";
}
/**
* Ajoute un composant Vue.JS à l'intérieur d'une zone de Vue.
*
* @param string $name
* @param array $props
*
* @return string
*/
public function component(string $name, array $props): string
{
if (!$this->inVue) {
return '<div class="alert alert-danger"><strong>Attention</strong> : votre composant doit être positionné à l\'intérieur' .
'd\'une zone dédiée à Vue.js. Veuillez utiliser $this->begin(); avant et $this->end(); après.</div>';
}
$name = str_replace('/', '-', $name);
$attrs = [];
foreach ($props as $pn => $pv) {
$pt = getType($pv);
switch ($pt) {
case 'boolean':
$pv = $pv ? 'true' : 'false';
break;
case 'array':
case 'object':
$pv = json_encode(Axios::extract($pv));
default:
$pv = (string)$pv;
}
$attrs[':' . strtolower(preg_replace('/(?<!^)[A-Z]/', '-$0', $pn))] = $pv;
}
$res = $this->getView()->tag($name, $attrs)->html('');
return $res;
}
}
\ No newline at end of file
<?php
namespace UnicaenVue\View\Helper;
use UnicaenVue\View\Helper\VueViewHelper;
use Psr\Container\ContainerInterface;
/**
* Description of VueViewHelperFactory
*
* @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
*/
class VueViewHelperFactory
{
/**
* @param ContainerInterface $container
* @param string $requestedName
* @param array|null $options
*
* @return VueViewHelper
*/
public function __invoke(ContainerInterface $container, $requestedName, $options = null): VueViewHelper
{
$viewHelper = new VueViewHelper();
return $viewHelper;
}
}
\ No newline at end of file
<?php
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment