Skip to content

⚽️ Projeto simples de um portal de notícias esportivas utilizando PHP

License

Notifications You must be signed in to change notification settings

heitor-am/SportNews

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Documentação 📄

Projeto simples de um Portal de Notícias sobre Esporte ⚽️.


🏁 Tópicos


✅ Pré-requisitos

Antes de começar, você vai precisar ter instalado em sua máquina as seguintes ferramentas:

  • Apache
  • MySQL
  • PHP

Além disto é bom ter um editor para trabalhar com o código como VSCode


🎲 Preparando o Banco de Dados

0️⃣ Acesse o diretório onde vai ficar o projeto, no terminal/cmd:

$ cd /var/www/html/ 

1️⃣ Clone este repositório:

$ git clone https://github.com/heitor-am/SportNews

2️⃣ Vá para o diretório db:

$ cd /SportNews/db

3️⃣ Execute o arquivo init.sql, no MySQL:

$ sudo mysql

mysql> source /var/www/html/SportNews/db/init.sql;

Pronto, agora o Banco de Dados está preparado para executar o arquivo SportNews/index.php.


🛠 Tecnologias

As seguintes ferramentas foram usadas na construção do projeto:


🏠 Homepage

No arquivo index.php, onde é tratado a homepage do site, logo no início do código, é feita uma requisição ao banco de dados.

<?php
require_once 'db/db_connect.php';

$query = "SELECT * FROM articles ORDER BY id DESC LIMIT 0,7";
$result = mysqli_query($connect, $query);

while ($row = mysqli_fetch_assoc($result)) {
    $array[] = $row;
}

mysqli_close($connect);
?>

Nessa requisição, os sete últimos artigos publicados no Banco de Dados são colocados em um array associativo, que por sua vez, é usado para manipular a distribuição das notícias ao longo da página.

Como exemplo, tem-se o artigo principal da página:

O artigo principal sempre é o mais recente, portanto, está na posição “0” no array, e, dessa forma, é posto na página, distribuindo as informações no HTML conforme as keys do array associativo (‘title’, ‘time’, ‘subtitle’, …).

<section id="banner">
    <div class="content">
        <header>
            <h1><?php echo $array[0]['title']; ?></h1>
            <p><?php echo $array[0]['time']; ?></p>
        </header>
        <p><?php echo $array[0]['subtitle']; ?></p>
        <ul class="actions">
            <li><a id="<?php echo $array[0]['id'];?>" class="button big" style="cursor: pointer;" href="articles/article.php?id=<?php echo $array[0]['id']; ?>">Sobre</a></li>
        </ul>
    </div><span class="image object"><img src="<?php echo $array[0]['main_image']; ?>" alt=""></span>
</section>

E isso acontece com todos os demais artigos que aparecem na homepage.


🔏 Login e Artigos

Para que o redator possa criar um novo artigo, ele primeiro precisa logar no sistema. A opção para login fica na sidebar.

Clicando no botão, um pop-up aparece na tela onde são solicitadas as informações de login: Nome do usuário e Senha.

<!-- Modal -->
<div id="modal-login" class="modal">
    <!-- Conteúdo do modal -->
    <form class="modal-content" action="http://localhost/SportNews/redator/login.php" method="POST">
        <div class="imgcontainer">
            <span onclick="document.getElementById('modal-login').style.display='none'" class="close" title="Close Modal">&times;</span>
            <img src="http://localhost/SportNews/img/avatar.png" alt="Avatar" class="avatar">
        </div>
        <div class="container">
            <label for="uname">Nome do usuário</label>
            <input type="text" placeholder="Digite o nome de usuário" name="uname" required>

            <label for="psw">Senha</label>
            <input type="password" placeholder="Digite a senha" name="psw" required>
        </div>

        <div class="container" style="background-color:#f1f1f1">
            <button type="submit" name="btn-login">Login</button>
            <span class="psw">Esqueceu a <a onclick="alerta('question', 'Esqueceu sua senha?', 'Por favor, entre em contato com o administrador da página!');">senha?</a></span>
        </div>
    </form>
</div>

<script>
    // Pegar o modal
    var modal = document.getElementById('modal-login');

    // Quando o usuário clicar em qualquer lugar fora do modal, feche-o
    window.onclick = function(event) {
        if (event.target == modal) {
            modal.style.display = "none";
        }
    }
</script>

Ao clicar no botão Login, todas as informações inseridas nos inputs são enviadas, através do método POST, ao arquivo /redator/login.php, onde é verificado se os dados recebidos estão contidos no Banco de dados.

if (isset($_POST['btn-login'])) {
    $errors = array();
    $username = mysqli_escape_string($connect, $_POST['uname']);
    $password = mysqli_escape_string($connect, $_POST['psw']);

    $query = "SELECT username FROM redator WHERE username = '$username'";
    $result = mysqli_query($connect, $query);

    if (mysqli_num_rows($result) > 0) {
        $password = md5($password);
        $query = "SELECT * FROM redator WHERE username = '$username' AND password = '$password'";

        $result = mysqli_query($connect, $query);

        if (mysqli_num_rows($result) == 1) {
            $redator = mysqli_fetch_array($result);
            mysqli_close($connect);
            $_SESSION['logado'] = true;
            $_SESSION['user_id'] = $redator['id'];
            header('Location: ./');
        } else {
            $errors[] = "Senha incorreta";
        }
    } else {
        $errors[] = "Usuário inexistente";
    }
}

if (!empty($errors)) {
    foreach ($errors as $error) {
        alerta("error", "Oops...", $error);
        echo "<script>setTimeout('javascript:fechar();',3500);</script>";
      
    }
}

Se as informações forem autenticadas, o redator será direcionado à página de edição de artigos.

Após preencher os campos de texto, o redator clica em Preview, onde os inputs são inseridos dentro de um array, e acaba sendo direcionado a uma página que mostra uma pré-visualização do seu artigo, dessa forma, ele pode verificar o resultado final sem que haja a necessidade de enviar ao Banco de Dados.

$content = array(
    "category" => $_POST["category"],
    "editor" => $_POST["editor"],
    "main_image" => $_POST["main_image"],
    "title" => $_POST["title"],
    "subtitle" => $_POST["subtitle"],
    "content" => $_POST["content"]
);

Trecho do código preview.php, onde os conteúdos dos inputs são inseridos em um array.

Observe que há duas opções: Voltar, caso o redator não tenha gostado do resultado do artigo, e Enviar, caso a demonstração tenha sido aprovada.

Ao clicar em Enviar, o array com o conteúdo do artigo é enviado ao arquivo /redator/commit_data.php, através do método POST.

<body>
    <div id="main">
        <div class="inner">
            <form action="commit_data.php" method="post">
                <div id="preview-content">
                    <section>
                        <header class="main">
                            <h1><?php echo $content['title']; ?></h1>
                            <h2><?php echo $content['subtitle']; ?></h2>
                            <p><?php echo "Por " . $content['editor'] . "<br><time>" . $time . "</time>"; ?></p>
                        </header>

                        <?php echo $_POST["content"]; ?>
                    </section>
                </div>
                <a href="./">Voltar</a>
                <input type="submit" value="Enviar">
            </form>
        </div>
    </div>
</body>

Trecho do código preview.php, onde o conteúdo do artigo é preparado para ser enviado ao arquivo commit_data.php

Obs: O arquivo commit_data.php depende do arquivo /db/db_connect.php, onde é estabelecida a conexão com o Banco de Dados.

<?php
// Conexão com BD
$servername = "localhost";
$username = "sportnews";
$password = "sportnews";
$database = "sportnews";

$connect = mysqli_connect($servername, $username, $password, $database);

if (mysqli_connect_error()) {
    echo "Connection failed: " . mysqli_connect_error();
}

No arquivo commit_data.php, onde os dados foram recebidos, é feito um INSERT na base de dados com todo o conteúdo do artigo.

$now = date("d/m/Y H:i");

$query = "INSERT INTO articles (category, editor, time, main_image, title, subtitle, content) VALUES (" .
      "'" . $content["category"] . "'," .
      "'" . $content["editor"] . "'," .
      "'" . $now . "'," .
      "'" . $content["main_image"] . "'," .
      "'" . $content["title"] . "'," .
      "'" . $content["subtitle"] . "'," .
      "'" . $content["content"] . "');";

if (mysqli_query($connect, $query)) {
      alerta("success", "Artigo enviado com sucesso!", false);
      echo "<script>setTimeout('javascript:fechar();',3500);</script>";

      // Encerrando a sessão
      session_start();
      session_unset();
      session_destroy();
} else {
      echo "Error: " . $query . "<br>" . mysqli_error($connect);
}

A tabela articles possui a seguinte estrutura:


🎯 Página do artigo

Cada link de artigo localizado na homepage tem seu botão de redirecionamento para a apresentação completa, o botão sobre.

Ao clicar no botão, o usuário é direcionado ao arquivo /articles/article.php, tendo como parâmetro o id do artigo.

<ul class="actions">
            <li><a id="<?php echo $array[0]['id'];?>" class="button big" style="cursor: pointer;" href="articles/article.php?id=<?php echo $array[0]['id']; ?>">Sobre</a></li>
</ul>

Recebendo o id do artigo, o arquivo article.php faz uma REQUEST no Banco de Dados para que as informações daquele artigo em específico sejam extraídas.

if (isset($_GET['id'])) {
    $id = $_GET['id'];
    $id = mysqli_escape_string($connect, $id);

    $query = "SELECT * FROM articles WHERE id=$id";
    $result = mysqli_query($connect, $query);

    while ($row = mysqli_fetch_assoc($result)) {
        echo "<section><header class='main'>" .
            "<h1 style='margin-bottom: 5px;'>" . $row['title'] . "</h1>" .
            "<h2 style='$subtitle_style'>" . $row['subtitle'] . "</h2>" .
            "<p>Por " . $row['editor'] . "<br><time>" . $row['time'] . "</time></p></header>" .
            $row['content'] . "</section>";
    }

    mysqli_close($connect);
}

🔎 Sistema de pesquisa

Caso o usuário procure por um artigo específico, ele pode optar por digitar uma palavra de seu interesse na barra de pesquisa localizada na sidebar.

Ao digitar uma palavra e pressionar a tecla Enter, o formulário, onde está localizado a barra de pequisa, aciona o arquivo /articles/search.php, que, por sua vez, através do método GET, recebe o conteúdo do input e, logo após, faz uma requisição à Base de Dados para receber todos os artigos que possuam o título relacionado ao que foi digitado na barra de pesquisa.

if (isset($_GET['query'])) {
  $search = $_GET['query'];
  $search = mysqli_escape_string($connect, $search);

  $query = "SELECT * FROM articles WHERE title LIKE '%$search%' ORDER BY id DESC";
  $result = mysqli_query($connect, $query);

  while ($row = mysqli_fetch_assoc($result)) {
    $array[] = $row;
  }

  mysqli_close($connect);
}

Caso a busca seja favorável, os artigos envolvidos são enviados à tela.

<?php
if (!empty($array)) {
    echo "<div class='posts'>";
    foreach ($array as $a) {
    echo "<article><a class='image' style='cursor: pointer;'><img src=" . $a['main_image'] . " alt=''></a>" .
        "<h3>" . $a['title'] . "</h3>" .
        "<p>" . $a['time'] . "</p>" .
        "<ul class='actions'>" .
        "<li><a id='". $a['id'] ."' class='button' " . 
        "href='./article.php?id=" . $a['id'] . "'style='cursor: pointer;'>Sobre</a></li></ul></article>";
    }
    echo "</div>";
} else {
    echo "<div style='text-align:center;margin-top:1.5em;'><p style='font-size:24px;margin-bottom:10px;'>Nenhum resultado encontrado :(</p>" .
        "<p style='font-size:1em;'>Sua busca por \"" . $search . "\" não retornou resultados.<br>" .
        "Tente novamente com outros termos.</p></div>";
}
?>

🏀 Filtragem por categoria

Na sidebar, o usuário pode filtrar os artigos existentes com base em sua categoria.

Ao clicar em uma das categorias listadas, o usuário é redirecionado a https://localhost/SportNews/articles/?cat={categoria}, onde a categoria escolhida é passada como parâmetro em cat, e, dessa forma, o usuário verá apenas os artigos da categoria escolhida.

<li><a href="http://localhost/SportNews/articles/?cat=Futebol">Futebol</a></li>
<li><a href="http://localhost/SportNews/articles/?cat=NBA">NBA</a></li>
<li><a href="http://localhost/SportNews/articles/?cat=eSports">eSports</a></li>
<li><a href="http://localhost/SportNews/articles/?cat=Vôlei">Vôlei</a></li>

📊 API

A API – Interface de Programação de Aplicações – escolhida para o projeto foi a https://v2.api-football.com, exclusivamente utilizada para a coleta de dados atualizados do campeonato brasileiro de futebol.

O arquivo de conexão à API está em /tables/api.php

<?php
session_start();
$code = $_SESSION['code'];

// COLETA DE DADOS DA API

$curl = curl_init();

// serie-a = 1396
// serie-b = 1397
// serie-c = 1472
// serie-d = 1476

curl_setopt_array($curl, [
	CURLOPT_URL => "https://v2.api-football.com/leagueTable/$code",
	CURLOPT_RETURNTRANSFER => true,
	CURLOPT_FOLLOWLOCATION => true,
	CURLOPT_ENCODING => "",
	CURLOPT_MAXREDIRS => 10,
	CURLOPT_TIMEOUT => 30,
	CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
	CURLOPT_CUSTOMREQUEST => "GET",
	CURLOPT_HTTPHEADER => [
		"x-rapidapi-host: v2.api-football.com",
		"x-rapidapi-key: 9e34581cb10ffc42a0527ed497342f96"
	],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
	echo "cURL Error #:" . $err;
} else {
	$content_php = json_decode($response);
	$table_values = $content_php->api->standings[0];
}

Os dados retirados da API são inseridos em um array, para que o tratamento das informações possa ser feito de maneira mais eficiente, e depois são jogados em uma tabela gerada por tags HTML, que pode ser vista em http://localhost/SportNews/tables/?serie=a ou http://localhost/SportNews/tables/?serie=b, isso ocorre no arquivo /tables/index.php.

<section id="banner">
    <div class="content">
        <header class="major">
            <h2>Tabela Brasileirão 2020 - <?php echo $table_values[0]->group ?></h2>
        </header>
        <div class="table-wrapper">
            <table>
                <thead>
                    <tr>
                        <th>Clube</th>
                        <th>Pts</th>
                        <th>PJ</th>
                        <th>VIT</th>
                        <th>E</th>
                        <th>DER</th>
                        <th>GP</th>
                        <th>GC</th>
                        <th>SG</th>
                    </tr>
                </thead>
                <tbody>
                    <?php
                    for ($x = 0; $x < 20; $x++) {
                        echo "<tr><td style=min-width:250px;><div style=display:inline-block;width:30px;>" . $table_values[$x]->rank .
                            "</div><div style=display:inline-block;><img src=" . $table_values[$x]->logo . " style=width:20px;margin-bottom:-5px;margin-right:20px;> " .
                            $table_values[$x]->teamName . "</div></td>" .
                            "<td>" . $table_values[$x]->points . "</td>" .
                            "<td>" . $table_values[$x]->all->matchsPlayed . "</td>" .
                            "<td>" . $table_values[$x]->all->win . "</td>" .
                            "<td>" . $table_values[$x]->all->draw . "</td>" .
                            "<td>" . $table_values[$x]->all->lose . "</td>" .
                            "<td>" . $table_values[$x]->all->goalsFor . "</td>" .
                            "<td>" . $table_values[$x]->all->goalsAgainst . "</td>" .
                            "<td>" . $table_values[$x]->goalsDiff . "</td></tr>";
                    }
                    ?>
                </tbody>
            </table>
        </div>
</section>

👨🏻‍💻 Economia de código

Como uma boa prática de código limpo, todas as páginas do site foram feitas usando-se o header e o footer localizados em /config/header.php e /config/footer.php, respectivamente.

include 'config/header.php';
include 'config/footer.php';

📝 Licença

Este projeto está sob a licença MIT.