Página AMP (I)

Primero, la plantilla:

<!doctype html>
<html amp lang="es">
    <head>
        <meta charset="utf-8">
        <script async src="https://cdn.ampproject.org/v0.js"></script>
        <title>Juan Pablo Fuentes</title>
        <link rel="canonical" href="index.html">
        <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
        <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
    </head>
    <body>
    </body>
</html>

Comprobamos que valida añadiendo ‘#development=1’ al final de la url. Debería salir por consola lo siguiente:

AMP validation successful.

Vamos a añadir antes del amp-boilerplate los metadatos:

  <script type="application/ld+json">
            {
            "@context": "http://schema.org",
            "@type": "WebSite",
            "headline": "Juan Pablo Fuentes WebSite",
            "datePublished": "2018-06-13T12:02:41Z",
            "image": [
            "img/photo.jpg"
            ]
            }
        </script>

El índice va a consistir en tres imágenes una al lado de otra que nos enlazarán con tres secciones. Para ello añadimos el html:

<h1>Juan Pablo Fuentes</h1>
        <div id="contenido">
            <div>
                <a href="#"><amp-img src="img/programador.jpg" alt="Programador" height="512" width="512" layout="responsive" ></amp-img><p>Programador</p></a></div>
            <div>
                <a href="#"><amp-img src="img/profesor.jpg" alt="Profesor" height="512" width="512"layout="responsive" ></amp-img><p>Profesor</p></a></div>
            <div>
                <a href="#"><amp-img src="img/narrador.jpg" alt="Narrador" height="512" width="512" layout="responsive"></amp-img><p>Narrador</p></a></div>
        </div>

Ponemos el css que controlará el comportamiento. En este caso la página canónica y la amp son la misma, así que usamos mediaqueries para controlar posición y tamaño:

<style amp-custom>
            /* Cualquier estilo personalizado va aquí */
            body {
                background: linear-gradient(to right, #00B4DB, #B3E4EB);
                font-family: Verdana;
            }
            #contenido{
                display:flex;
                align-items: center;
                height: 70vh;

            }
            #contenido div{
                margin:50px;
                flex-grow: 1;
                box-shadow: 5px 5px 15px #BBBBBB;
                background-color: white;
                border:1px solid #BBBBBB;
            }
            #contenido div p{
                text-align: center;

            }
            a{text-decoration: none;
              color:#444444;}
            a:hover{
                font-weight: bold;
            }
            h1{
                text-align: center;
                color:#444444;
            }
            @media screen and (max-width: 600px) {
                #contenido {
                    flex-direction: column;
                }
                #contenido div{
                    width:70%;
                }
            }
        </style>

AMP

Para empezar deberíamos ir a la página oficial:

https://www.ampproject.org/es/

Aquí la plantilla básica:

https://www.ampproject.org/es/docs/getting_started/quickstart

Y aquí una lista de componentes:

https://ampbyexample.com/

Un tutorial interesante:

https://webdesign.tutsplus.com/tutorials/how-to-make-a-basic-amp-page-from-scratch–cms-26158

Plantillas:

https://www.ampstart.com/

Plugin para wordpress:

https://es.wordpress.org/plugins/accelerated-mobile-pages/

Ejemplos de consultas

 /**
     * @return Producto[] Returns an array of Producto objects
     */
    public function findByPrecioMayor($value) {
        return $this->createQueryBuilder('c')
                        ->andWhere('c.precio > :val')
                        ->setParameter('val', $value)
                        ->orderBy('c.id', 'ASC')
                        ->getQuery()
                        ->getResult()
        ;
    }

    /**
     * @return Producto[] Returns an array of Producto objects
     */
    public function findByNombre($value) {
        return $this->createQueryBuilder('c')
                        ->select('c.nombre')
                        ->where('c.nombre like :val')
                        ->setParameter('val', "%$value%")
                        ->orderBy('c.id', 'ASC')
                        ->getQuery()
                        ->getArrayResult()
        ;
    }

    /**
     * @return Producto[] Returns an array of Producto objects
     */
    public function countProductos() {
        return $this->createQueryBuilder('c')
                        ->select('count(c) as total')
                        ->getQuery()
                        ->getSingleScalarResult()
        ;
    }
  /**
     * @return Producto[] Returns an array of Producto objects
     */
    public function countProductosByTipo() {
        return $this->createQueryBuilder('c')
                        ->select('c.tipo, count(c) as total')
                ->groupBy('c.tipo')
                        ->getQuery()
                        ->getArrayResult()
        ;
    }

Añadir entidades relacionadas

Si tenemos una entidad producto relacionada con categorías, y queremos dar de alta un producto, tendremos que indicar cual es su categoría y esto lo tenemos que hacer con entidades. Si la categoría no existe lo hacemos así:

  $categoria=new Categoria();
        $categoria->setNombre('Tornillería');
        
        $producto = new Producto();
        $producto->setNombre('Tornillo');
        $producto->setPrecio(20);
        $producto->setCategoria($categoria);
        
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($categoria);
        $entityManager->persist($producto);
        $entityManager->flush();

Si la categoría ya existe lo hacemos así:

 $categoria = $this->getDoctrine()
                ->getRepository(Categoria::class)
                ->find(1);

        $producto = new Producto();
        $producto->setNombre('Arandela');
        $producto->setPrecio(30);

           $producto->setCategoria($categoria);
        
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($producto);
        $entityManager->flush();

o así:

 $categoria = $this->getDoctrine()
                ->getRepository(Categoria::class)
                ->find(1);

        $producto = new Producto();
        $producto->setNombre('Arandela');
        $producto->setPrecio(30);

        $categoria->addProducto($producto);
        
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($producto);
        $entityManager->flush();

Para acceder a los productos de una categoría lo podemos hacer así:

  $categoria = $this->getDoctrine()
                ->getRepository(Categoria::class)
                ->find(1);

        $productos = $categoria->getProductos();
        foreach($productos as $producto){
            echo $producto->getNombre();
        }

Obtener entidades desde la base de datos

Teniendo bien configurado el archivo .env, nos colocamos en la raiz de nuestro sitio y ponemos lo siguiente:

 php bin/console doctrine:mapping:import App\Entity annotation --path=src/Entity

Y después regeneramos las entidades para que se pongan los getters y setters:

php bin/console make:entity --regenerate App

Ejemplos de plantillas

Controlador:

 public function index()
    {
        $tabla=['a','b','c'];
        $usuario="Juan";
        $notas=[1,5,23,78,5];
        $actores=['Penelope','Alec','Fernando'];
        return $this->render('plantillas/index.html.twig', [
            'tabla' => $tabla,
            'usuario'=>$usuario,
            'notas'=>$notas,
            'actores'=>$actores
        ]);
    }

Vista:

{% extends 'base.html.twig' %}


{% block body %}
    {# funciones de twig #}

    {{max(1,2,3)}}
    {{max(tabla)}}

    {{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
    {{ random('ABC') }}                         {# example output: C #}
    {{ random() }}                              {# example output: 15386094 (works as the native PHP mt_rand function) #}
    {{ random(5) }}                             {# example output: 3 #}
    {{ random(10) }}                             {# example output: 3 #}

    {{date()|date('d/m/Y')}}
    {{date('2020-1-2')|date('d/m/Y')}}
    {{date('-2days')|date('d/m/Y')}}
    {{date('+2month')|date('d/m/Y')}}

    {# filtros usando | (pipe) #}

    {{usuario}}
    {{usuario | upper}}
    {{usuario | lower}}
    {{usuario | reverse}}
    {{tabla|sort|join(', ')|upper}}


    {% set cadena='4,2,8,4,1,9,34' %}

    {{cadena | split(',') |sort|join(',')}}
    <table>
        <tr><td>Nombre</td></tr>
        {% for actor in actores %}
            <tr><td>{{actor}}</td></tr>
        {% endfor %}
    </table>

    {% set a=5 %}
    {% set b=[1,2,3,4] %}
    {% set c={'nombre':'Juan','Apellidos':'Pi'} %}

    {{a +3}}
    {{a * 3}}
    {{a // 4}} {# division entera #}
    {{a / 4}} {# division normal #}
    {{a **2}} {# potencia (elevar a un número #}
    {{a % 2}}

    {% if a>10 %}
        <h1>A es mayor que 10</h1>
    {% else %}
        <h1>A es menor que 10</h1>
    {% endif %}

    {% if a>10 %}
        <h1>A es mayor que 10</h1>
    {% elseif a>5 %}
        <h1>A es menor que 5</h1>
    {% else %}
        <h1>A vale muy poco</h1>
        <h2>Todo el código que quiera</h2>
    {% endif %}

    {# >,<,>=,<=,!=,==,===, starts width ends with matches #}

    {% if usuario starts with 'J' %}
        <p>Usuario empieza con j</p>
    {% endif %}

    {% if b[0] matches '/\\d+/' %}
        <p>Es numérico</p>
    {% endif %}

    {% if 2 in b %}
        <p>Tenemos 2 en b</p>
    {% endif %}

    {% if 2 is even %}
        <p>Tenemos 2 en b</p>
    {% endif %}
    <ul>
        {% for i in 0..10 if i is odd %}
            <li>{{i}}</li>
            {% endfor %}
            {% for i in 'a'..'z' %}
            <li>{{i}}</li>
            {% endfor %}

        {% for i in b %}
            <li>{{i}}</li>
            {% endfor %}

        {% for i in tabla %}
            <li>{{i}}</li>
            {% endfor %}
    </ul>


{% endblock %}

Symfony ecuación segundo grado

Controlador:

/**
     * @Route("/areas/ecuacion", name="ecuacion")
     */
    public function ecuacion() {
        $request = Request::createFromGlobals();
        $a = $request->request->get('a');
        $b = $request->request->get('b', 1);
        $c = $request->request->get('c', 1);
        if (!is_numeric($c) || !is_numeric($b) || !is_numeric($a)) {
            return $this->render('areas/error.html.twig',
                ['mensaje' => 'Los valores deben ser numéricos',
                'url'=>'/areas']);
        }
        $raiz = $b * $b - 4 * $a * $c;
        if ($raiz < 0) {
            $raiz1 = "No existe";
            $raiz2 = "No existe";
        } elseif ($raiz == 0) {
            $raiz1 = -$b / ($a * 2);
            $raiz2 = $raiz1;
        } else {
            $raiz1 = (-$b + sqrt($raiz)) / ($a * 2);
            $raiz2 = (-$b - sqrt($raiz)) / ($a * 2);
        }

        return $this->render('areas/ecuacion.html.twig', ['raiz1' => $raiz1,
                    'raiz2' => $raiz2,
                    'valores'=>[$a,$b,$c]]);
    }

Vistas:

  <form action="areas/ecuacion" method="post">
        <input type="number" name="a">x<sup>2</sup>+ <input type="number" name="b"> x+
        <input type="number" name="c"><br/>
        <input type="submit">
    </form>
{% extends 'base.html.twig' %}

{% block title %}Hello!{% endblock %}

{% block body %}
    <h1>Las soluciones de la ecuación {{valores[0]}}x<sup>2</sup>+{{valores[1]}}x+{{valores[2]}} son:</h1>
    {% if raiz1=='No existe' %}
        <p>La ecuación no tiene soluciones reales, quizás imaginarias</p>
   <!--     <img src="{{ asset('img/ninguna.gif') }}" alt=""/>-->

    {% elseif raiz1==raiz2 %}
        <p>La ecuación tiene una sóla raiz: {{raiz1}}</p>
   <!--     <img src="{{ asset('img/una.png') }}" alt=""/>-->
    {% else %}
        <p>Raiz 1: {{raiz1}}</p>
        <p>Raiz 2: {{raiz2}}</p>
    <!--    <img src="{{ asset('img/dos.png') }}" alt=""/>-->
    {% endif %}
    <iframe src="http://graph.tk/" id="my_graph" style="width:500px;height:400px">
</iframe>
<script>
var my_graph=document.getElementById("my_graph");

my_graph.onload=function(){
    function g(m){
        my_graph.contentWindow.postMessage(m,"http://graph.tk");
    };
    g("add:{{valores[0]}}x^2+{{valores[1]}}x+{{valores[2]}}");
    g("center:0,0");
}

</script>
{% endblock %}

Symfony obtener parámetros

Controlador:

   /**
     * @Route("/areas/cuadrado", name="cuadrado")
     */
    public function cuadrado() {
        $request = Request::createFromGlobals();
        $lado = $request->request->get('lado');
        return $this->render('areas/cuadrado.html.twig', ['lado' => $lado]);
    }

Vista:

   <h1>Cálculo de áreas</h1>
    <a href="/areas/cuadrado">Cuadrado</a>
    <form action="/areas/cuadrado" method="post">
        <input type="text" name="lado"><input type="submit">
    </form>

Resultado:

{% extends 'base.html.twig' %}

{% block title %}Hello!{% endblock %}

{% block body %}
    <h1>El área del cuadrado es el lado al cuadrado</h1>
    <p>Un cuadrado de lado {{lado}} tiene un área de {{lado*lado}}</p>
{% endblock %}

Symfony pasar html a la vista

Controlador:

/**
     * @Route("/tabla/pintar", name="pintar")
     */
    public function pintar() {
        $request = Request::createFromGlobals();
        $ancho = $request->query->get('ancho');
        $alto = $request->query->get('alto');
        $tabla = "<table>";
        for ($i = 0; $i < $alto; $i++) {
            $tabla .= "<tr>";
            for ($j = 0; $j < $ancho; $j++) {
                $tabla .= "<td>" . ($j + $i * $ancho) . "</td>";
            }
            $tabla .= "</tr>";
        }
        $tabla .= "</table>";
        return $this->render('tabla/tabla.html.twig', [
                    'tabla' => $tabla,
        ]);
    }

vista:

{% extends 'base.html.twig' %}

{% block title %}Tabla{% endblock %}
  {% block styles %}
      <style>
          footer{
              background-color: red;
          }
          </style>
  {% endblock %}
{% block body %}
{# esto es un comentario #}
    <h1>Esta es la tabla que ha pedido</h1>
  
    {{tabla | raw}}
    <img src="{{ asset('img/gato.jpg') }}" alt=""/>

{% endblock %}

CRUD Producto Symfony

Entidad Producto:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ProductoRepository")
 */
class Producto
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $nombre;

    /**
     * @ORM\Column(type="integer")
     */
    private $precio;

    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    private $stock;

    
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getNombre(): ?string
    {
        return $this->nombre;
    }

    public function setNombre(string $nombre): self
    {
        $this->nombre = $nombre;

        return $this;
    }

    public function getPrecio(): ?int
    {
        return $this->precio;
    }

    public function setPrecio(int $precio): self
    {
        $this->precio = $precio;

        return $this;
    }

    public function getStock(): ?int
    {
        return $this->stock;
    }

    public function setStock(?int $stock): self
    {
        $this->stock = $stock;

        return $this;
    }
}

Controlador:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Producto;

class ProductoController extends AbstractController {

    /**
     * @Route("/producto", name="producto")
     */
    public function index() {
        /*
          $entityManager = $this->getDoctrine()->getManager();

          for ($i = 1; $i <= 10; $i++) {
          $producto = new Producto();
          $producto->setNombre('Producto '.$i);
          $producto->setPrecio($i);

          // Guardamos el producto
          $entityManager->persist($producto);
          }
          // Actualizamos los datos en la BD
          $entityManager->flush();
         */
        /*
          //Producto es un objeto del tipo Producto Entity
          //Con sus propiedades y métodos (concretamente los get y set de las columnas)
          $producto = $this->getDoctrine()
          ->getRepository(Producto::class)
          ->find($id);
          $producto->setNombre('Nuevo nombre cool para mi producto ¡Mola!');
          $entityManager = $this->getDoctrine()->getManager();
          $entityManager->remove($producto);
          $entityManager->flush();

          $producto = $this->getDoctrine()
          ->getRepository(Producto::class)
          ->findOneBy(['nombre'=>'Producto 5']);


         * 
         */
        $productos = $this->getDoctrine()
                ->getRepository(Producto::class)
                ->findAll();
        $salida = [];
        foreach ($productos as $producto) {
            $salida[] = [$producto->getId(),
                $producto->getNombre(),
                $producto->getPrecio()];
        }
        return $this->render('producto/index.html.twig', [
                    'productos' => $salida]);
    }

    /**
     * @Route("/producto/nuevo", name="nuevo")
     */
    public function nuevo() {
        $request = Request::createFromGlobals();
        $nombre = $request->request->get('nombre');
        $precio = $request->request->get('precio');
        $stock = $request->request->get('stock');
        $id = '';
        if (!empty($nombre) && !empty($precio)) {
            //Añadir el producto
            $producto = new Producto();
            $producto->setNombre($nombre);
            $producto->setPrecio($precio);
            if (empty($stock)) {
                $stock = 0;
            }
            $producto->setStock($stock);
            $entityManager = $this->getDoctrine()->getManager();
            // Guardamos el producto
            $entityManager->persist($producto);

            // Actualizamos los datos en la BD
            $entityManager->flush();
            $id = $producto->getId();
        }
        return $this->render('producto/nuevo.html.twig', ['id' => $id]);
    }

    /**
     * @Route("/producto/eliminar/{id}", name="eliminar")
     */
    public function eliminar(Producto $producto) {

        $nombre = $producto->getNombre();

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->remove($producto);
        $entityManager->flush();

        return $this->render('producto/eliminar.html.twig', ['nombre' => $nombre]);
    }

    /**
     * @Route("/producto/editar/{id}", name="editar")
     */
    public function editar(Producto $producto) {

        $request = Request::createFromGlobals();
        $nombre = $request->request->get('nombre');
        $precio = $request->request->get('precio');
        $stock = $request->request->get('stock');
        if (!empty($nombre) && !empty($precio)) {
            $producto->setNombre($nombre);
            $producto->setPrecio($precio);
            if (empty($stock)){$stock=0;}
            $producto->setStock($stock);
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->flush();
        }
        return $this->render('producto/editar.html.twig', [
                    'nombre' => $producto->getNombre(),
                    'precio' => $producto->getPrecio(),
                    'stock' => $producto->getStock(),
        ]);
    }

}

Plantillas:

index:

{% extends 'base.html.twig' %}

{% block title %}{% endblock %}

{% block body %}
    <div class="container">
        <h1>Productos</h1>
        <a href="/producto/nuevo" class="btn btn-success">Nuevo producto</a>
        <table class="table">
            <tr><td>Id</td><td>Nombre</td><td>Precio</td><td>Acciones</td></tr>
            {% for producto in productos %}
                <tr><td>{{producto[0]}}</td><td>{{producto[1]}}</td><td>{{producto[2]}}</td>
                    <td><a href="/producto/editar/{{producto[0]}}" class="btn btn-info">Editar</a> 
                        <a href="/producto/eliminar/{{producto[0]}}" class="btn btn-danger">Eliminar</a></td></tr> 
                {% endfor %}
        </table>
    </div>
{% endblock %}

nuevo

{% extends 'base.html.twig' %}

{% block title %}{% endblock %}

{% block body %}
    <div class="container">
        <h1>Nuevo Producto</h1>
        {% if id %}
            <div class="alert alert-success alert-dismissible">
                <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
                <strong>Correcto</strong> Se ha insertado con éxito el producto con id {{id}}.
            </div>
        {% endif %}

        <form method="post">
            <div class="form-group">
                Nombre: <input  class="form-control" type="text" name="nombre"></div>
            <div class="form-group">
                Precio: <input  class="form-control" type="text" name="precio"></div>
            <div class="form-group">
                Stock: <input  class="form-control" type="text" name="stock"></div>
            <input type="submit" class="btn btn-info">
        </form>
    </div>
{% endblock %}

editar:

{% extends 'base.html.twig' %}

{% block title %}{% endblock %}

{% block body %}
    <div class="container">
        <h1>Editar Producto</h1>
      

        <form method="post">
            <div class="form-group">
                Nombre: <input  class="form-control" type="text" value="{{nombre}}" name="nombre"></div>
            <div class="form-group">
                Precio: <input  class="form-control" type="text" value="{{precio}}" name="precio"></div>
            <div class="form-group">
                Stock: <input  class="form-control" type="text" value="{{stock}}" name="stock"></div>
            <input type="submit" class="btn btn-info">
        </form>
            <a href="/producto">Volver al listado</a>
    </div>
{% endblock %}

eliminar:

{% extends 'base.html.twig' %}

{% block title %}{% endblock %}

{% block body %}
    <div class="container">
        <h1>Eliminar Producto</h1>
        <p>Has eliminado el producto <b>{{nombre}}</b></p>
        <a href="/producto">Volver al listado</a>
    </div>
{% endblock %}