Como usar o MongoDB com PHP

Nesta parte do tutorial do aplicativo Bookshelf em PHP, você aprende a criar, ler, atualizar e excluir dados estruturados em um banco de dados do MongoDB.

Esta página faz parte de um tutorial com várias páginas. Para começar do início e ver as instruções de configuração, consulte App Bookshelf em PHP.

Como executar o MongoDB no Compute Engine

Se você ainda não tiver configurado uma instância do MongoDB, crie uma instância gerenciada no Google Cloud usando o Atlas no Google Cloud ou o mLab. Se preferir usar uma instância não gerenciada do MongoDB no Compute Engine, confira a solução click-to-deploy pré-configurada da Bitnami.

Para um melhor desempenho, certifique-se de que a instância do MongoDB esteja hospedada no Google Cloud e localizada na mesma região do seu aplicativo.

Por padrão, o MongoDB usa a porta 27017 para comunicação. Depois de configurar sua instância do MongoDB, talvez seja necessário configurar as regras de firewall para permitir o tráfego de entrada e saída para essa porta. Para instâncias executadas no Google Cloud, consulte Como usar regras de firewall, para receber instruções

Instalar e ativar o MongoDB para PHP

  1. Para se conectar ao MongoDB enquanto executa o aplicativo Bookshelf localmente, instale a extensão do MongoDB para PHP (em inglês). Se você estiver usando Linux, instale com o PECL.

  2. Ative a extensão em php.ini:

    Linux/macOS

    echo "extension=mongodb.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
    

    Windows

    Adicione a seguinte linha a php.ini:

    extension=php_mongodb.dll
    
  3. Adicione a biblioteca do MongoDB ao Cloud Composer:

    composer require "mongodb/mongodb:^1.0.0"
    

Como definir configurações

  1. Vá para o diretório getting-started-php/2-structured-data e copie o arquivo settings.yml.dist:

    cp config/settings.yml.dist config/settings.yml
    
  2. Abra config/settings.yml para edição.

  3. Substitua o [YOUR_PROJECT_ID] pelo ID do projeto do Google Cloud.

  4. Defina o valor de bookshelf_backend como mongodb.

  5. Defina mongo_url, mongo_database e mongo_collection com os valores apropriados para sua instância do MongoDB. Por exemplo:

    mongo_url: mongodb://104.197.3.232:27017
    mongo_database: getting_started_php
    mongo_collection: books
    
  6. Salve e feche settings.yml.

Como instalar dependências

No diretório 2-structured-data, digite este comando:

composer install

Como executar o app na máquina local

  1. Inicie um servidor da Web local:

    php -S localhost:8000 -t web
    
  2. No navegador, digite este endereço:

    http://localhost:8000

Agora, navegue nas páginas da Web do aplicativo e adicione, edite e exclua livros.

Como implantar o app no ambiente flexível do App Engine

  1. Na janela do terminal, implante o aplicativo de amostra:

    gcloud app deploy
    
  2. No navegador, digite este endereço. Substitua o [YOUR_PROJECT_ID] pelo ID do projeto do Google Cloud.

    https://[YOUR_PROJECT_ID].appspot.com
    

Se você atualizar seu aplicativo, implante a versão atualizada inserindo o mesmo comando usado na primeira vez que implantou o aplicativo. A nova implantação cria uma versão do seu aplicativo e a promove à versão padrão.

As versões mais antigas do aplicativo serão mantidas, assim como as instâncias de máquina virtual (VM, na sigla em inglês) associadas. Lembre-se de que essas versões do aplicativo e instâncias de VM são recursos passíveis de cobrança.

Para reduzir custos, exclua as versões não padrão do app.

Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Consulte mais detalhes em Limpeza.

Estrutura do app

Veja no diagrama a seguir os componentes do aplicativo e como eles se encaixam.

Estrutura e processo de implantação do aplicativo Bookshelf

Noções básicas sobre o código

Anteriormente, você editou settings.yml e definiu o valor de bookshelf_backend como mongodb. Essa configuração avisa ao aplicativo para carregar a classe MongoDb, definida em src/DataModel/MongoDb.php. A classe MongoDb une a API MongoDB e armazena livros no banco de dados MongoDB.

O código a seguir em controllers.php define e registra um gerenciador para a rota GET '/books'. A variável $model é uma instância da classe MongoDb. O método $model->listBooks retorna uma matriz que contém uma matriz de livros e um cursor. Em seguida, o mecanismo do modelo Twig (em inglês) renderiza a lista de livros de acordo com o modelo list.html.twig:

$app->get('/books/', function (Request $request) use ($app) {
    /** @var DataModelInterface $model */
    $model = $app['bookshelf.model'];
    /** @var Twig_Environment $twig */
    $twig = $app['twig'];
    $token = $request->query->get('page_token');
    $bookList = $model->listBooks($app['bookshelf.page_size'], $token);

    return $twig->render('list.html.twig', array(
        'books' => $bookList['books'],
        'next_page_token' => $bookList['cursor'],
    ));
});

Este é o modelo do Twig para listar livros recuperados do banco de dados do Cloud SQL. O modelo recebe uma variável de matriz chamada books. Para cada livro na matriz, são exibidos o título e o autor. O modelo também recebe uma variável next_page_token que determina se o botão Mais será exibido:

{% for book in books %}
<div class="media">
  <a href="/books/{{book.id}}">
    <div class="media-left">
      <img src="http://placekitten.com/g/128/192">
    </div>
    <div class="media-body">
      <h4>{{book.title}}</h4>
      <p>{{book.author}}</p>
    </div>
  </a>
</div>
{% else %}
<p>No books found</p>
{% endfor %}

O código a seguir define e registra um gerenciador para a rota GET '/books/{id}', em que {id} é o ID de um livro individual. O gerenciador chama o método $model->read para buscar o livro especificado do Cloud SQL. O mecanismo do modelo do Twig renderiza o livro de acordo com o modelo view.html.twig:

$app->get('/books/{id}', function ($id) use ($app) {
    /** @var DataModelInterface $model */
    $model = $app['bookshelf.model'];
    $book = $model->read($id);
    if (!$book) {
        return new Response('', Response::HTTP_NOT_FOUND);
    }
    /** @var Twig_Environment $twig */
    $twig = $app['twig'];

    return $twig->render('view.html.twig', array('book' => $book));
});

O modelo view.html.twig recebe uma variável chamada book e mostra o título do livro, data da publicação, autor e descrição:

<div class="media">
  <div class="media-body">
    <h4 class="book-title">
      {{book.title}}
      <small>{{book.published_date}}</small>
    </h4>
    <h5 class="book-author">By {{book.author|default('Unknown', True)}}</h5>
    <p class="book-description">{{book.description}}</p>
  </div>
</div>

Quando o usuário clicar em Adicionar livro, o gerenciador de GET /books/add exibirá um formulário para inserir o título, o autor e outras informações sobre ele. Quando o usuário clicar em Salvar, o gerenciador de POST /books/add receberá o novo livro da solicitação e chamará $model->create para armazená-lo no Cloud SQL:

$app->get('/books/add', function () use ($app) {
    /** @var Twig_Environment $twig */
    $twig = $app['twig'];

    return $twig->render('form.html.twig', array(
        'action' => 'Add',
        'book' => array(),
    ));
});

$app->post('/books/add', function (Request $request) use ($app) {
    /** @var DataModelInterface $model */
    $model = $app['bookshelf.model'];
    $book = $request->request->all();
    $id = $model->create($book);

    return $app->redirect("/books/$id");
});

Este é o modelo de formulário de entrada do livro:

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

{% block content %}
<h3>{{action}} book</h3>

<form method="POST" enctype="multipart/form-data">

  <div class="form-group">
    <label for="title">Title</label>
    <input type="text" name="title" id="title" value="{{book.title}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="author">Author</label>
    <input type="text" name="author" id="author" value="{{book.author}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="published_date">Date Published</label>
    <input type="text" name="published_date" id="published_date" value="{{book.published_date}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="description">Description</label>
    <textarea name="description" id="description" class="form-control">{{book.description}}</textarea>
  </div>

  <button id="submit" type="submit" class="btn btn-success">Save</button>
</form>

{% endblock %}

O código de amostra inclui gerenciadores extras para a edição e exclusão de livros individuais:

$app->get('/books/{id}/edit', function ($id) use ($app) {
    /** @var DataModelInterface $model */
    $model = $app['bookshelf.model'];
    $book = $model->read($id);
    if (!$book) {
        return new Response('', Response::HTTP_NOT_FOUND);
    }
    /** @var Twig_Environment $twig */
    $twig = $app['twig'];

    return $twig->render('form.html.twig', array(
        'action' => 'Edit',
        'book' => $book,
    ));
});

$app->post('/books/{id}/edit', function (Request $request, $id) use ($app) {
    $book = $request->request->all();
    $book['id'] = $id;
    /** @var DataModelInterface $model */
    $model = $app['bookshelf.model'];
    if (!$model->read($id)) {
        return new Response('', Response::HTTP_NOT_FOUND);
    }
    if ($model->update($book)) {
        return $app->redirect("/books/$id");
    }

    return new Response('Could not update book');
});
$app->post('/books/{id}/delete', function ($id) use ($app) {
    /** @var DataModelInterface $model */
    $model = $app['bookshelf.model'];
    $book = $model->read($id);
    if ($book) {
        $model->delete($id);

        return $app->redirect('/books/', Response::HTTP_SEE_OTHER);
    }

    return new Response('', Response::HTTP_NOT_FOUND);
});