mkdir vmsymfony
vagrant init bento/ubuntu-16.04
Adicionar IP para acesso externo:
config.vm.network :private_network, ip: "192.168.100.200"
Logar na nova VM:
vagrant up
vagrant ssh
Usar PHP em versões superiores >= 7.1.
Dica: usar o PPA do ondrej:
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get update
Instalação do PHP:
sudo apt-get -y install php7.2
Bibliotecas mínimas para o symfony:
sudo apt-get -y install php7.2-xml php7.2-intl php7.2-mbstring
Bibliotecas de conexão para bancos de dados:
sudo apt-get -y install php7.2-pgsql php7.2-mysql php7.2-sqlite3
Instalação do composer globalmente:
curl -s https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
Instalação do git:
sudo apt-get install -y git
Configurações globais:
git config --global user.name "joazinho"
git config --global user.email "joaozinho@usp.br"
Dependências mínimas:
sudo apt-get -y install mariadb-server
Criando usuário e banco:
sudo mysql
CREATE DATABASE uspdev;
GRANT ALL PROVILEGES ON uspdev.* to uspdev@'localhost' identified by 'uspdev';
quit
Dependências mínimas:
sudo apt-get -y install postgresql
Criando usuário e banco:
sudo su posgtres
psql
CREATE USER uspdev WITH PASSWORD 'uspdev';
CREATE DATABASE uspdev OWNER uspdev;
\q
exit
Projeto symfony e suas dependências:
composer create-project symfony/skeleton uspdev
cd usp
composer require doctrine maker annotations twig form validator
composer require encore asset webprofiler server web-server-bundle
Exemplo de entrada no .env para postgresql e mysql:
DATABASE_URL="pgsql://uspdev:uspdev@localhost:5432/uspdev?charset=utf8"
DATABASE_URL="mysql://uspdev:uspdev@localhost:3306/uspdev"
Commit das mudanças:
git init
git add --all
git commit -m 'The best project in the world is alive'
git push origin master
Criar seguintes issues no projeto:
Checkout para branch master para issue1-RedeEntity
git checkout -b issue1-RedeEntity
php bin/console make:entity Rede
Aplicar schema no banco de dados:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
devmaster:
issue2: Exemplo de get e set pata o campo nome:
public function setNome($nome)
{
$this->nome = $nome;
}
public function getNome()
{
return $this->nome;
}
Tarefa:
Um equipamento pode pertencer a uma rede, certo? Então, na entidade Equipamento:
/**
* @ORM\ManyToOne(targetEntity="Rede", inversedBy="equipamentos")
* @ORM\JoinColumn(nullable=true)
*/
private $rede;
/* get e set */
public function setRede($rede)
{
$this->rede = $rede;
}
public function getRede()
{
return $this->rede;
}
Relacionamento inverso na entidade de Rede:
use Doctrine\Common\Collections\ArrayCollection;
public function __construct()
{
$this->equipamentos = new ArrayCollection();
}
/**
* @ORM\OneToMany(targetEntity="Equipamento",mappedBy="rede")
*/
private $equipamentos;
/* get e set */
public function setEquipamentos($equipamentos)
{
$this->equipamentos = $equipamentos;
}
public function getEquipamentos()
{
return $this->equipamentos;
}
Criar issues sobre controllers:
Checkout para branch master para issues5-6-7-EquipamentoNewAction
git checkout -b issues5-6-7-EquipamentoNewAction
Criar controller para Equipamento:
php bin/console make:controller EquipamentoController
Subir um server local:
php -S 0.0.0.0:8888 -t public/
php bin/console server:run *:8888
Form para cadastro de equipamentos:
php bin/console make:form EquipamentoType
Adicionar os campos no form criado:
$builder->add('patrimonio')->add('macaddress')->add('local')
->add('vencimento');
use App\Entity\Equipamento;
use App\Form\EquipamentoType;
Controler:
/**
* @Route("/equipamento/new", name="equipamento_new")
*/
public function newAction()
{
$equipamento = new Equipamento();
$form = $this->createForm(EquipamentoType::class,$equipamento);
return $this->render('equipamento/new.html.twig', array(
'form' => $form->createView(),
));
}
Criar um arquivo de template
templates/equipamento/new.html.twig:
<html><body>
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" value="Cadastrar" />
{{ form_end(form) }}
</body></html>
Acessar rota no browser:
http://127.0.0.1:8000/equipamento/new
Sim, está horrível, mas aguardem!
Tratar a requisição no controller:
-> use Symfony\Component\HttpFoundation\Request;
-> public function newAction(Request $request)
Persistir campos no banco de dados:
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($equipamento);
$em->flush();
return $this->redirectToRoute('equipamento_new');
}
Persistiu?
php bin/console doctrine:query:sql "select * from equipamento;"
Finalizando issues:
Criação, distribuição e pull/merge requests das issues entre os/as devs:
Boom? Método __toString na entity Rede:
public funtion __toString() {
return $this->nome;
}
Cadastrar novo equipamento
nova issue: showAction e indexAction para EquipamentoController:
/**
* @Route("/equipamento/{id}", name="equipamento_show")
*/
public function showAction(Equipamento $equipamento)
{
return $this->render('equipamento/show.html.twig', array(
'equipamento' => $equipamento,
));
}
Criar arquivo equipamento/show.html.twig:
<html><body>
{{ equipamento.macaddress}}
{{ equipamento.vencimento|date('d/m/Y') }}
</body></html>
Criação de página que lista todos equipamentos:
/**
* @Route("/equipamento", name="equipamento_index")
*/
public function indexAction()
{
$repository = $this->getDoctrine()->getRepository(Equipamento::class);
$equipamentos = $repository->findAll();
return $this->render('equipamento/index.html.twig', array(
'equipamentos' => $equipamentos,
));
}
Criar arquivo equipamento/index.html.twig:
<html><body><ul>
{% for equipamento in equipamentos %}
<li> <a href="{{ path('equipamento_show', { 'id': equipamento.id }) }}"> {{equipamento.macaddress}}</a> </li>
{% endfor %}
</ul></body></html>
Filtrando equipamentos ativos via repository:
public function ativos()
{
$now = new \DateTime();
return $this->createQueryBuilder('e')
->where('e.vencimento >= :now')
->setParameter('now', $now)
->getQuery()
->getResult();
}
Corrigir controller
$equipamentos = $repository->ativos();
Ok, você quer usar SQL na raça...
public function ativosSql()
{
$now_obj = new \DateTime();
$now = $now_obj->format('Y-m-d H:i:s');
$conn = $this->getEntityManager()->getConnection();
$sql = 'SELECT * FROM equipamento e WHERE e.vencimento >= :now';
$stmt = $conn->prepare($sql);
$stmt->execute(['now' => $now]);
return $stmt->fetchAll();
}
No Controller:
$equipamentos = $repository->ativosSql();
Criação, distribuição e pull/merge requests das issues entre os/as devs:
Criação das seguintes issues para o/a devmaster:
Edição dos registros de equipamento:
/**
* @Route("/equipamento/{id}/edit", name="equipamento_edit")
*/
public function editAction(Request $request, Equipamento $equipamento)
{
$form = $this->createForm(EquipamentoType::class,$equipamento);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('equipamento_show',['id' => $equipamento->getId()]);
}
return $this->render('equipamento/edit.html.twig', array(
'equipamento' => $equipamento,
'form' => $form->createView(),
));
}
Colocar link em equipamento/show.html.twig:
<a href="{{ path('equipamento_edit', { 'id': equipamento.id }) }}">Editar</a>
Para deletar, sério, nunca faça isso:
/**
* @Route("/equipamento/{id}/delete", name="equipamento_delete")
*/
public function deleteAction(equipamento $equipamento) {
$em = $this->getDoctrine()->getManager();
$em->remove($equipamento);
$em->flush();
return $this->redirectToRoute('equipamento_index');
}
The Right Way: Criar um formulário para enviar requisição HTTP DELETE
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
...
private function createDeleteForm(Equipamento $equipamento)
{
$id = $equipamento->getId();
return $this->createFormBuilder()
->setAction($this->generateUrl('equipamento_delete', ['id' => $id]))
->setMethod('DELETE')
->add('message',CheckboxType::class, [
'label' => "Quer mesmo deletar ?",
'required' => true,])
->getForm();
}
Método HTTP DELETE
/**
* @Route("/equipamento/{id}/delete", name="equipamento_delete",methods="DELETE")
*/
public function deleteAction(Request $request, Equipamento $equipamento)
{
$form = $this->createDeleteForm($equipamento);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($equipamento);
$em->flush();
}
return $this->redirectToRoute('equipamento_index');
Tela de confirmação:
/**
* @Route("/equipamento/{id}/delete/confirm", name="equipamento_delete_confirm")
*/
public function confirmDeleteAction(Equipamento $equipamento)
{
$form = $this->createDeleteForm($equipamento);
return $this->render('equipamento/delete.html.twig', array(
'equipamento' => $equipamento,
'form' => $form->createView(),
));
}
No template:
{{ form_start(form) }}
<button type="submit">Deletar</button>
{{ form_end(form) }}
Criação, distribuição e pull/merge requests das issues entre os/as devs:
Dica para deixar equipamentos órfãos:
$equipamentos = $rede->getEquipamentos();
foreach($equipamentos as $equipamento){
$equipamento->setRede(null);
}
Modelo para dhcp.conf para múltiplas networks:
ddns-update-style none;
default-lease-time 86400;
max-lease-time 86400;
authoritative;
option domain-name-servers 143.107.253.3,143.107.253.5;
shared-network "default" {
subnet 10.0.184.0 netmask 255.255.255.0 {
range 10.0.184.2 10.0.184.254;
option routers 10.0.184.1;
option broadcast-address 10.0.184.255;
deny unknown-clients;
host cliente1 {
hardware ethernet 00:1C:C0:99:0A:04;
fixed-address 10.0.184.51;
}
}
subnet 10.0.188.0 netmask 255.255.255.0 {
range 10.0.188.2 10.0.188.254;
option routers 10.0.188.1;
option broadcast-address 10.0.188.255;
deny unknown-clients;
host cliente2 {
hardware ethernet 00:1C:C0:98:FB:3C;
fixed-address 10.0.188.69;
}
}
}
Controller que monta o dhcp.conf:
/**
* @Route("/dhcpconf", name="dhcpconf")
*/
public function dhcpconf()
{
$response = new Response('teste');
$response->headers-> set('Content-Type', 'text/plain');
return $response;
}
Em src/Service/Dhcpconf.php
namespace App\Service;
class Dhcpconf
{
public function build()
{
return "ok, I amworking";
}
}
No Controller:
use App\Service\Dhcpconf;
$dhcpconf = new Dhcpconf();
$dhcpconf->build();
Necessidades: A partir do ip da rede e cidr:
Instalando e usando biblioteca externa:
#https://github.com/S1lentium/IPTools
sudo apt-get install php7.2-bcmath
composer require s1lentium/iptools
Classes disponíveis nessa lib:
use IPTools\IP;
use IPTools\Network;
Array com IPs do range:
$network = Network::parse('192.168.1.0/24');
$ips = [];
foreach($network as $ip) {
array_push($ips,(string)$ip);
}
// todo: gateway,broadcast,range_begin,range_end
Issues:
Tenha instalado:
php7.1+, composer, mysql e git*
composer create-project symfony/skeleton symfony_basico
symfony require maker
symfony require doctrine
composer require theofidry/psysh-bundle
create database symfony; grant all privileges on symfony.* to symfony@localhost identified by 'symfony'; inserir informações no .env
php bin/console make:entity Post php bin/console doctrine:migrations:diff php bin/console doctrine:migrations:migrate show tables
php bin/console make:entity Comment php bin/console doctrine:migrations:diff php bin/console doctrine:migrations:migrate
Adicionar os campos title e content na entity Post (as public) Adicionar os campos title, content, author_email e post_id (unsigned e referenciando um post) na entity Comment (as public) @ORM\ManyToOne(targetEntity="Rede", inversedBy="equipamentos")
use Doctrine\Common\Collections\ArrayCollection; public function __construct() { $this->comments = new ArrayCollection(); } /**
* @ORM\OneToMany(targetEntity="Equipamento",mappedBy="rede")
*/
private $equipamentos;
Array collection em POst apontando para Comments
php bin/console doctrine:migrations:diff php bin/console doctrine:migrations:migrate show tables desc Comment; desc Post;
php bin/console psysh
$post = new App\Entity\Post $post->title = "primeiro post" $post->content = "texto maravilhoso sobre um super post."
ls $em = $container->get('doctrine')->getManager() $em->persist($post) $em->flush()
$p = $em->getRepository('App\Entity\Post') $posts = $p->findAll()
foreach ($posts as $post) echo $post->title
$comment = new App\Entity\Comment $comment->author_email = 'fulano@eu.com' $comment->title = 'meu comentário' $comment->content = 'Gostei muito do seu post' $comment->post_id = $posts['id'==1] $em->persist($comment) em->flush()
select * from comment;
$comment2 = new App\Entity\Comment $comment2->author_email = 'ciclano@eu.com' $comment2->title = 'meu comentário 2' $comment2->content = 'Eu não gostei' $comment2->post = $posts['id'==1] $em->persist($comment2) $em->flush()
select * from comment;
$c = $em->getRepository('App\Entity\Comment') $comments = $c->findAll()
select * from Comment;
$p->find(1)->comments->toArray()
$c->find(2) $c->find(2)->post $c->find(2)->post->title
[extra] $em->createQuery('SELECT A.title FROM App:Post A WHERE A.id=2')->getResult()
$qb = $em->createQueryBuilder() $qb->select('u')->from('App\Entity\Post', 'u')->where('u.id = 2')->getQuery()->getResult()
Criar um método em src/Repository/CommentRepository.php e testar no console
####################################################3
public function up()
{
Schema::create('authors', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email');
$table->string('bio')->nullable();
$table->timestamps();
});
}
Colocar a coluna de chave estrangeira em database/migrations/add_author_id_to_posts.php
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->integer('author_id')->unsigned();
$table->foreign('author_id')->references('id')->on('authors');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('author_id');
});
}
}
app/Post.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function comments()
{
return $this->hasMany('App\Comment');
}
public function author()
{
return $this->belongsTo('App\Author');
}
}
app/Author.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
public function posts()
{
return $this->hasMany('App\Post');
}
public function comments()
{
return $this->hasManyThrough('App\Comment', 'App\Post');
}
}
- php artisan tinker
- $author = new Author
- $author->name = 'Leandro Ramos'
- $author->email = 'leandroramos@usp.br'
- $author->save()
- $post = new Post
- $post->title = 'Um Grande Post!'
- $post->content = 'Um excelente texto explicando sobre uma grande novidade'
- $post->author_id = 1
- $post = new Post
- $post->title = 'Segundo Grande Post!'
- $post->content = 'Segundo texto espetacular sobre um grande post.'
- $post->author_id = 1
- $post = new Post
- $post->title = 'Um Grande Post!'
- $post->content = 'Um excelente texto explicando sobre uma grande novidade'
- $post->author_id = 1
- $post->save()
- $post = new Post
- $post->title = 'Segundo Grande Post!'
- $post->content = 'Segundo texto espetacular sobre um grande post.'
- $post->author_id = 1
- $post->save()
- $comment = new Comment
- $comment->author_email = 'comentador@site.com'
- $comment->content = 'Mas que post sensacional!'
- $comment->post_id = 1
- $comment->save()
- $comment = new Comment
- $comment->author_email = 'oriboncina@site.com'
- $comment->content = 'Sei não... estás enganado!!'
- $comment->post_id = 1
- $comment->save()
- $comment = new Comment
- $comment->author_email = 'eu@site.com'
- $comment->content = 'Estão faltando alguns links no post.'
- $comment->post_id = 1
- $comment->save()
- $comment = new Comment
- $comment->author_email = 'eu@site.com'
- $comment->content = 'Obrigado, ajudou muito.'
- $comment->post_id = 2
- $comment->save()
-
- Mais testes:
- $author = Author::first()
- $author->posts
- $author->comments
- $author->comments->where('post_id', '2')
- $comment = Comment::first()
- $comment->post
- $comment->post->author
- $comment->post->author->name
- php artisan make:factory AuthorFactory --model="App\\Author"
- php artisan make:factory PostFactory --model="App\\Post"
- php artisan make:factory CommentFactory --model="App\\Comment"
AuthorFactory
<?php
use Faker\Generator as Faker;
$factory->define(App\Author::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'bio' => $faker->paragraph(1),
];
});
PostFactory
<?php
use App\Author;
use Faker\Generator as Faker;
$factory->define(App\Post::class, function (Faker $faker) {
return [
'title' => $faker->sentence(4),
'content' => $faker->paragraph(4),
'author_id' => function () {
return Author::orderByRaw("RAND()")
->take(1)
->first()
->id;
}
];
});
CommentFactory
<?php
use App\Post;
use Faker\Generator as Faker;
$factory->define(App\Comment::class, function (Faker $faker) {
return [
'author_email' => $faker->unique()->safeEmail,
'content' => $faker->paragraph(2),
'post_id' => function () {
return Post::orderByRaw("RAND()")
->take(1)
->first()
->id;
}
];
});
database/seeds/DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// $this->call(UsersTableSeeder::class);
echo "Criando 10 autores...\n";
factory(App\Author::class, 10)->create();
echo "Criando 36 posts relacionados a autores aleatórios...\n";
factory(App\Post::class, 36)->create();
echo "Criando 67 comentários relacionados a posts aleatórios...\n";
factory(App\Comment::class, 67)->create();
}
}