Desenvolvemento en PHP con Symfony: Diferenzas entre revisións

De Wiki do Ciclo ASIR do IES de Rodeira
Saltar á navegación Saltar á procura
 
(Non se amosan 69 revisións do historial feitas polo mesmo usuario.)
Liña 1: Liña 1:
<small>Esta guía se basa en Symfony 4.4<ref group="notas" name="n1" >Esta guía utilizará Symfony 4.4 porque a versión soportada na actual Debian Stable bullseye.</ref></small>
== Instalación e Configuración ==
== Instalación e Configuración ==
Requerimentos:
<small>Esta guía se basa en Symfony 5.5</small>
* apt install php-symfony
* apt install composer


=== Inicio da Aplicación ===
dfjadfñad
composer create-project symfony/website-skeleton:"^4.4" my_project_directory
<ref>Esta guía utilizará Symfony 5.4 porque é a última versión que soporta PHP 7.4. A partir de Symfony 6.0 se necesita php 8.0 ou superior.

</ref>
composer create-project symfony/skeleton:"^4.4" my_project_directory
* annotations
composer require sensio/framework-extra-bundle
composer require symfony/webpack-encore-bundle
yarn install


After installing Encore, your app already has a few files, organized into an assets/ directory:


assets/app.js

assets/bootstrap.js

assets/controllers.json

assets/styles/app.css

assets/controllers/hello_controller.js


With Encore, think of your app.js file like a standalone JavaScript application: it will require all of the dependencies it needs (e.g. jQuery or React), including any CSS. Your app.js file is already doing this with a JavaScript import statement:

Construye el CSS y JS con webpack
yarn watch || yarn dev || yarn build

Engadimos jquery
yarn add jquery --dev

"Recompilamos" css y js
yarn dev
<source lang='js'>
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/

// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';

// start the Stimulus application
import './bootstrap';


import $ from 'jquery'

// import funcion_exportada from "./nombrefichero"

$(document).ready(function() {
alert("Start !");
});
</source>

===Stimulus & Symfony UX===

[https://stimulus.hotwired.dev/handbook/introduction Stimulus] é un framework sinxelo para dotar de interactividade as páxinas. Baséase nos seguintes conceptos clave:
* Controllers
;; O "Controller" é o código Javascript encargado de levar a cabo a acción
* Actions
;; O "Action" é a acción a levar a cabo (unha funcionalidade do Controller)
* Targets
;; Un "Target" é un elemento HTML sobre o que pode actuar o "Controller"
* Values
;; Un "Value" é un dato que podemos ler, modificar ou observar mediante código no controller.

Todos estes obxectos se configurarán mediante atributos nas etiquetas HTML:
<source lang="html">
<div data-controller="clipboard">
PIN: <input data-clipboard-target="source" type="text" value="1234" readonly>
<button data-action="click->clipboard#copy">Copy to Clipboard</button>
</div>
</source>
Neste exemplo, o Controller será "clipboard", que será unha clase que hereda de Controller. Esta clase implementará as funcinalidades que se requerirán como "Actions". As action indicarán mediante un nome o método do Controller a utilizar, separando mediante # do nome do controller. As 'Action' teñen o formato evento->controlador#método

Os "Target" se crean mediante o atributo data-controller-target="nome", e se recuperarán no Controller mediante ''this.nomeTarget''. Os nomes de todos os targets deben estar nun array estático targets. Stimulus creará un atributo '''nomeTarget''' por cada elemento no array.

Cando se inicializa o controlador, se executará o método intialize(), mentres que cada vez que o Controller se "conecta" co documento, se invocará o connect() do controller. Cando o Controller se desconecte da páxina se invocará ao método disconnect()

O aspecto mínimo dun Controller stimulus é o seguinte:

<source lang="javascript">
// src/controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
}
</source>

Un controlador "completo" para o código anterior sería:
<source lang="javascript">
// src/controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = [ 'source' ]

copy(event) {
console.log("Copiando ao clipboard");
console.log("Os datos son ${this.source}!");
navigator.clipboard.writeText(this.source)
}

get source() {
return this.sourceTarget.value;
}
}

</source>

{{boxinfo|O controlasdor a executar se determina a partir do nome do ficheiro. Neste caso si o controller vai a corresponder ao HTML stimulus indicado anteriormente, debería chamarse ''clipboard_controller.js'' }}

Outros atributos que xestiona stimulus son:
* data-''controller''-''nome''-class
;; Esta propiedade está dispoñible no controlador a través do atributo this.''nome''Class. Precisamos dun atributo estático cun array dos distintos class creados chamado '''classes'''. Stimulus creará un atributo nomeClass para cada un deses elementos.
* Podemos facer uso dos atributos HTML data- que podemos recuperar no atributo ''dataset'' do elemento
* Mediante data-''controller''-''name''-param podemos indicar parámetros a pasarlle a "action" do controlador que recibiremos nun obxecto '''params'''.
* Mediante data-''controller''-''name''-value podemos definir atributos accesibles no controlador. Definimos no controlador un array ''''values''' indicando o atributo e o tipo, como por exemplo index:Number, url:String ou cliente:Object. Os tipos poden ser Array, Boolean, Number, String ou Object. Object e Array fan uso de JSON.stringify para codificar o valor e JSON.parse para decodificalo.

Os valores podemos obtelos con this.''name''Value, modificalos con this.''name''Value= ou verificar a súa existencia con this.has''name''Value. Creando un método ''name''ValueChanged(value,previousvalue) e posible executar código cada vez que o Value asociado sufre un cambio de valor.

E posible tamén poñer valores por defecto, para o caso de que a etiqueta HTML non inclúa un data-xxx-value...
<source lang="javascript">
static values = { index: { type: Number, default: 2 } }
static values = { index: Number, effect: { type: String, default: "kenburns" } }

/* efecto=this.effectValue; gardaría en efecto "kenburns" si o HTML non tivera un campo data-controller-effect-value */

</source>

facendo uso de [https://developer.mozilla.org/es/docs/Web/API/Fetch_API/Using_Fetch fetch] podemos realizar cargas asíncronas con moita facilidade. Vexamos un exemplo que trae html do servidor de xeito asíncrono e o refresca a un intervalo de tempo..
<source lang="html">
<!-- Descarga messages.html de xeito asíncrono, refrescando cada 5 segundos -->
<div data-controller="content-loader"
data-content-loader-url-value="/messages.html"
data-content-loader-refresh-interval-value="5000"></div>
</source>

<source lang="javascript">
export default class extends Controller {
static values = { url: String, refreshInterval: Number }
connect() {
this.load()

if (this.hasRefreshIntervalValue) {
this.startRefreshing()
}
}

startRefreshing() {
this.refreshTimer=setInterval(() => {
this.load()
}, this.refreshIntervalValue)
}

stopRefreshing() {
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
}
}

load() {
fetch(this.urlValue)
.then(response => response.text())
.then(html => this.element.innerHTML = html)
}

disconnect() {
this.stopRefreshing();
}
// …
}
</source>

---
assets/controllers


Method Invoked by Stimulus…
initialize() Once, when the controller is first instantiated
connect() Anytime the controller is connected to the DOM
disconnect() Anytime the controller is disconnected from the DOM

<source lang='html' style='font-size:80%'>

<div {{ stimulus_controller('say-hello') }}>
<input type="text" {{ stimulus_target('say-hello', 'name') }}>

<button {{ stimulus_action('say-hello', 'greet') }}>
Greet
</button>

<div {{ stimulus_target('say-hello', 'output') }}></div>
</div>

<div data-controller="slideshow">
<button data-action="slideshow#previous"> ← </button>
<button data-action="slideshow#next"> → </button>

<div data-slideshow-target="slide">🐵</div>
<div data-slideshow-target="slide">🙈</div>
<div data-slideshow-target="slide">🙉</div>
<div data-slideshow-target="slide">🙊</div>
</div>

</source>

<source lang='js' style='font-size:80%'>


import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = [ "slide" ]

initialize() {
this.index = 0
this.showCurrentSlide()
}

next() {
this.index++
this.showCurrentSlide()
}

previous() {
this.index--
this.showCurrentSlide()
}

showCurrentSlide() {
this.slideTargets.forEach((element, index) => {
element.hidden = index != this.index
})
}
}



export default class extends Controller {
static targets = ['name', 'output']

connect() {
console.log("Hello, Stimulus!", this.element)
}

greet() {
this.outputTarget.textContent = `Hello ${this.nameTarget.value}!`
}
}
</source>

*https://symfony.com/doc/current/doctrine.html
*https://symfony.com/doc/current/doctrine/associations.html
*https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/query-builder.html
*https://www.doctrine-project.org/projects/doctrine-collections/en/stable/expression-builder.html
*https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html
*https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/query-builder.html
*https://symfonycasts.com/screencast/doctrine-queries/and-where-or-where
*https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html
*https://symfony.com/doc/current/doctrine.html#fetching-objects-from-the-database
*https://symfony.com/doc/4.4/doctrine.html#creating-an-entity-class

*https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/annotations-reference.html

===Programando la aplicación ===
* bin/console make:controller MainController
* bin/console make:Entity
* bin/console make:migration

php bin/console doctrine:migrations:migrate
This command executes all migration files that have not already been run against your database. You should run this command on production when you deploy to keep your production database up-to-date.

== Microservizos e APIs ==
* yarn add bootstrap --dev
* yarn add jquery @popperjs/core --dev


-----

*var module=require('modulename') importa un módulo Javascript
*import ... from ....
*export { function list, ... }
*export default function

Se crea el fichero JavaScript con el export al final
Se crea un fichero de importacion que importe los módulos del js y los haga globales:

import * as alias from 'modulo.js'
global.función = funcion importada

Se añade un addEntry('nombremodulo','path/file.js') a webpack.config.js

yarn dev
------

Para incluír bootstrap lo importamos con import en app.js y con @import en /assets/styles/app.css

== Notas ==
<small>
<references group="notas"/>
</small>

Revisión actual feita o 30 de outubro de 2022 ás 22:28

Esta guía se basa en Symfony 4.4[notas 1]

Instalación e Configuración

Requerimentos:

  • apt install php-symfony
  • apt install composer

Inicio da Aplicación

composer create-project symfony/website-skeleton:"^4.4" my_project_directory

composer create-project symfony/skeleton:"^4.4" my_project_directory

  • annotations

composer require sensio/framework-extra-bundle composer require symfony/webpack-encore-bundle yarn install


After installing Encore, your app already has a few files, organized into an assets/ directory:


assets/app.js

assets/bootstrap.js

assets/controllers.json

assets/styles/app.css

assets/controllers/hello_controller.js


With Encore, think of your app.js file like a standalone JavaScript application: it will require all of the dependencies it needs (e.g. jQuery or React), including any CSS. Your app.js file is already doing this with a JavaScript import statement:

Construye el CSS y JS con webpack yarn watch || yarn dev || yarn build

Engadimos jquery yarn add jquery --dev

"Recompilamos" css y js yarn dev

/*
 * Welcome to your app's main JavaScript file!
 *
 * We recommend including the built version of this JavaScript file
 * (and its CSS file) in your base layout (base.html.twig).
 */

// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';

// start the Stimulus application
import './bootstrap';


import $ from 'jquery'

// import funcion_exportada from "./nombrefichero"

$(document).ready(function() {
        alert("Start !");
});

Stimulus & Symfony UX

Stimulus é un framework sinxelo para dotar de interactividade as páxinas. Baséase nos seguintes conceptos clave:

  • Controllers
O "Controller" é o código Javascript encargado de levar a cabo a acción
  • Actions
O "Action" é a acción a levar a cabo (unha funcionalidade do Controller)
  • Targets
Un "Target" é un elemento HTML sobre o que pode actuar o "Controller"
  • Values
Un "Value" é un dato que podemos ler, modificar ou observar mediante código no controller.

Todos estes obxectos se configurarán mediante atributos nas etiquetas HTML:

<div data-controller="clipboard">
  PIN: <input data-clipboard-target="source" type="text" value="1234" readonly>
  <button data-action="click->clipboard#copy">Copy to Clipboard</button>
</div>

Neste exemplo, o Controller será "clipboard", que será unha clase que hereda de Controller. Esta clase implementará as funcinalidades que se requerirán como "Actions". As action indicarán mediante un nome o método do Controller a utilizar, separando mediante # do nome do controller. As 'Action' teñen o formato evento->controlador#método

Os "Target" se crean mediante o atributo data-controller-target="nome", e se recuperarán no Controller mediante this.nomeTarget. Os nomes de todos os targets deben estar nun array estático targets. Stimulus creará un atributo nomeTarget por cada elemento no array.

Cando se inicializa o controlador, se executará o método intialize(), mentres que cada vez que o Controller se "conecta" co documento, se invocará o connect() do controller. Cando o Controller se desconecte da páxina se invocará ao método disconnect()

O aspecto mínimo dun Controller stimulus é o seguinte:

// src/controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
}

Un controlador "completo" para o código anterior sería:

// src/controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
   static targets = [ 'source' ]

   copy(event) {
       console.log("Copiando ao clipboard");
       console.log("Os datos son ${this.source}!");
       navigator.clipboard.writeText(this.source)
   }

   get source() {
      return this.sourceTarget.value;
   }
}
Boxinfo info.png
O controlasdor a executar se determina a partir do nome do ficheiro. Neste caso si o controller vai a corresponder ao HTML stimulus indicado anteriormente, debería chamarse clipboard_controller.js

Outros atributos que xestiona stimulus son:

  • data-controller-nome-class
Esta propiedade está dispoñible no controlador a través do atributo this.nomeClass. Precisamos dun atributo estático cun array dos distintos class creados chamado classes. Stimulus creará un atributo nomeClass para cada un deses elementos.
  • Podemos facer uso dos atributos HTML data- que podemos recuperar no atributo dataset do elemento
  • Mediante data-controller-name-param podemos indicar parámetros a pasarlle a "action" do controlador que recibiremos nun obxecto params.
  • Mediante data-controller-name-value podemos definir atributos accesibles no controlador. Definimos no controlador un array 'values indicando o atributo e o tipo, como por exemplo index:Number, url:String ou cliente:Object. Os tipos poden ser Array, Boolean, Number, String ou Object. Object e Array fan uso de JSON.stringify para codificar o valor e JSON.parse para decodificalo.

Os valores podemos obtelos con this.nameValue, modificalos con this.nameValue= ou verificar a súa existencia con this.hasnameValue. Creando un método nameValueChanged(value,previousvalue) e posible executar código cada vez que o Value asociado sufre un cambio de valor.

E posible tamén poñer valores por defecto, para o caso de que a etiqueta HTML non inclúa un data-xxx-value...

  static values = { index: { type: Number, default: 2 } }
  static values = { index: Number, effect: { type: String, default: "kenburns" } }

  /* efecto=this.effectValue; gardaría en efecto "kenburns" si o HTML non tivera un campo data-controller-effect-value */

facendo uso de fetch podemos realizar cargas asíncronas con moita facilidade. Vexamos un exemplo que trae html do servidor de xeito asíncrono e o refresca a un intervalo de tempo..

<!-- Descarga messages.html de xeito asíncrono, refrescando cada 5 segundos -->
<div data-controller="content-loader"
     data-content-loader-url-value="/messages.html"
     data-content-loader-refresh-interval-value="5000"></div>
export default class extends Controller {
  static values = { url: String, refreshInterval: Number }
    connect() {
       this.load()

       if (this.hasRefreshIntervalValue) {
          this.startRefreshing()
       }
    }

  startRefreshing() {
    this.refreshTimer=setInterval(() => {
      this.load()
    }, this.refreshIntervalValue)
  }

  stopRefreshing() {
     if (this.refreshTimer) {
           clearInterval(this.refreshTimer);
     }
  }

  load() {
    fetch(this.urlValue)
      .then(response => response.text())
      .then(html => this.element.innerHTML = html)
  }

  disconnect() {
     this.stopRefreshing();
  }
  // …
}

--- assets/controllers


Method Invoked by Stimulus… initialize() Once, when the controller is first instantiated connect() Anytime the controller is connected to the DOM disconnect() Anytime the controller is disconnected from the DOM

<div {{ stimulus_controller('say-hello') }}>
    <input type="text" {{ stimulus_target('say-hello', 'name') }}>

    <button {{ stimulus_action('say-hello', 'greet') }}>
        Greet
    </button>

    <div {{ stimulus_target('say-hello', 'output') }}></div>
</div>

<div data-controller="slideshow">
  <button data-action="slideshow#previous"></button>
  <button data-action="slideshow#next"></button>

  <div data-slideshow-target="slide">🐵</div>
  <div data-slideshow-target="slide">🙈</div>
  <div data-slideshow-target="slide">🙉</div>
  <div data-slideshow-target="slide">🙊</div>
</div>
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]

  initialize() {
    this.index = 0
    this.showCurrentSlide()
  }

  next() {
    this.index++
    this.showCurrentSlide()
  }

  previous() {
    this.index--
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index != this.index
    })
  }
}



export default class extends Controller {
    static targets = ['name', 'output']

  connect() {
    console.log("Hello, Stimulus!", this.element)
  }

    greet() {
	this.outputTarget.textContent = `Hello ${this.nameTarget.value}!`
    }
}

Programando la aplicación

  • bin/console make:controller MainController
  • bin/console make:Entity
  • bin/console make:migration

php bin/console doctrine:migrations:migrate This command executes all migration files that have not already been run against your database. You should run this command on production when you deploy to keep your production database up-to-date.

Microservizos e APIs

  • yarn add bootstrap --dev
  • yarn add jquery @popperjs/core --dev



  • var module=require('modulename') importa un módulo Javascript
  • import ... from ....
  • export { function list, ... }
  • export default function

Se crea el fichero JavaScript con el export al final Se crea un fichero de importacion que importe los módulos del js y los haga globales:

import * as alias from 'modulo.js' global.función = funcion importada

Se añade un addEntry('nombremodulo','path/file.js') a webpack.config.js

yarn dev


Para incluír bootstrap lo importamos con import en app.js y con @import en /assets/styles/app.css

Notas

  1. Esta guía utilizará Symfony 4.4 porque a versión soportada na actual Debian Stable bullseye.