Ir para conteúdo
Fórum Script Brasil

Frank K Hosaka

Membros
  • Total de itens

    1.622
  • Registro em

  • Última visita

Tudo que Frank K Hosaka postou

  1. Já faz algum tempo que eu escrevi que é possível usar o <input> como se fosse uma calculadora, tipo 15+15. Isso não é verdade. Ele só funciona somente se o servidor trabalhar com o mysqli ou com o PDO. Ele não funciona com o Eloquent do Laravel. arquivo index.php <?php // mysql schema terminais orcamento(id,qt)(1,0) if(isset($_POST['qt'])) { $qt=$_POST['qt']; $mysqli=new mysqli("localhost","root","","terminais"); $mysqli->query("update orcamento set qt = $qt where id=1"); $pdo=new PDO("mysql:host=localhost;dbname=terminais","root",""); $stmt=$pdo->prepare("update orcamento set qt = :qt where id=1"); $stmt->bindParam(':qt',$qt); $stmt->execute(); } ?> <form method=post> <input value="15+15" name=qt> <input type=submit> </form> --------------------------- ambiente Laravel: arquivo routes.web.php <?php use App\Http\Controllers\Teste; use Illuminate\Support\Facades\Route; Route::view('/','teste'); Route::post('teste',[Teste::class,'inicio']); arquivo resources.views.teste.blade.php <form method=post action=teste> @csrf <input value="15+15" name=qt> <input type=submit> </form> arquivo app.Models.Orcamento.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Orcamento extends Model { use HasFactory; protected $table='orcamento'; public $timestamps = false; } arquivo app.Http.Controllers.Teste.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Orcamento; class Teste { function inicio(Request $request) { $qt=$request->qt; Orcamento::where('id',1)->update(['qt'=>$qt]); // resultado: SQLSTATE[01000]: Warning: 1265 Data truncated // for column 'qt' at row 1 (Connection: mysql, SQL: update `orcamento` // set `qt` = 15+15 where `id` = 1) } }
  2. Com a ajuda da Gemini, consegui o seguinte: resources.views.layouts.navigation.blade.php <style> .nav-link.active {color:#333;font-weight:bold} </style> <ul class="nav nav-tabs mt-2"> <li class='nav-item'> <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" class='nav-link active' style=margin-left:120px> Dashboard </x-nav-link> <li class='nav-item'> <x-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')" class='nav-link' style=margin-left:120px> Chirps </x-nav-link> <li class='nav-item' style=margin-left:120px> <div class=dropdown> <button class="btn btn-sm btn-outline-secondary d-inline-flex align-items-center dropdown-toggle" data-bs-toggle=dropdown> {{ Auth::user()->name }} </button> <div class=dropdown-menu> <x-dropdown-link :href="route('profile.edit')"> Profile </x-dropdown-link> <form method="POST" action="{{ route('logout') }}"> @csrf <x-dropdown-link :href="route('logout')" onclick="event.preventDefault();this.closest('form').submit();"> Log Out </x-dropdown-link> </form> </div> </div> </ul> <script> document.addEventListener('DOMContentLoaded', function() { const navLinks = document.querySelectorAll('.nav-link'); const selectedOption = localStorage.getItem('selectedOption'); // Aplica a classe 'active' ao elemento selecionado anteriormente navLinks.forEach(link => { if (link.textContent === selectedOption) { link.classList.add('active'); } else {link.classList.remove('active')} }); navLinks.forEach(link => { link.addEventListener('click', function() { navLinks.forEach(link => link.classList.remove('active')); this.classList.add('active'); localStorage.setItem('selectedOption', this.textContent); }); }); }); </script>
  3. Estou estudando o projeto Chirp do Laravel, mas eu desativei o Tailwinds e ativei o Bootstrap. O resultado é um completo desastre. As imagens do tutorial são bem diferentes do que eu consegui com a minha improvisação. Mas o meu maior problema é o arquivo <svg>, ele fica enorme, tornando o programa inoperante. Hoje a Gemini me ensinou a controlar o tamanho do <svg> assim <svg width=100 height=100>. Agora pedir para ela traduzir o Tailwinds em Bootstrap é pura perda de tempo, mas em compensação a brutal diferença visual entre os dois CSS é motivo suficiente para tirar o chapéu para o Tailwinds. O Tailwinds é capaz de mudar a cor da fonte de um texto, mais escuro para a opção que você escolher na barra de navegação, e menos escuro para as outras opções, e ele ainda é capaz de desenhar uma linha abaixo da opção selecionada. Isso é muito bacana, não sei se vou conseguir imitar o mesmo comportamento com o Bootstrap, por enquanto eu me contento com a posição dos elementos, é o máximo que eu consigo fazer. resources.views.chirps.index.blade.php <x-app-layout> <div class="container mt-2 bg-light"> <form method="POST" action="{{ route('chirps.index') }}"> @csrf <div class=row> <div class=col-md-12> <textarea name="message" placeholder="What's on your mind?" cols=50 class="mt-4">{{ old('message') }}</textarea> <x-input-error :messages="$errors->get('message')" class="" /> </div> <div class=col-md-auto> <x-primary-button class="btn btn-primary">Chirp</x-primary-button> </div> </div> </form> <div class=""> @foreach ($chirps as $chirp) <svg width=30 height=30 xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /> </svg> <span class="">{{ $chirp->user->name }}</span> <small class="">{{ $chirp->created_at->format('j M Y, g:i a') }}</small> <p class="">{{ $chirp->message }}</p> @endforeach </div> </div> </x-app-layout>
  4. Eu usei esse código: <?php if(isset($_FILES)) { var_dump($_FILES); } ?> <form method=post enctype="multipart/form-data"> <label for="arquivo">Selecione um arquivo:</label> <input type="file" id="arquivo" name="meuArquivo"> <input type=submit> </form> resultado: C:\wamp64\www\Astudy\index.php:4: array (size=1) 'meuArquivo' => array (size=6) 'name' => string 'xml_nfce_1725380458.zip' (length=23) 'full_path' => string 'xml_nfce_1725380458.zip' (length=23) 'type' => string 'application/x-zip-compressed' (length=28) 'tmp_name' => string 'C:\wamp64\tmp\php5066.tmp' (length=25) 'error' => int 0 'size' => int 2559750 Faz muito tempo que o meu zipArchive não funciona, sempre recebi a mensagem de que o arquivo zipado tinha 'error' => int 1, mas isso me deixou grilado: por que o PHP não consegue abrir o arquivo zipado, mas o Windows consegue? Consultando a Gemini, ela informou que existem vários motivos. O primeiro da lista é a configuração do PHP, ela pediu para ver o arquivo php.ini e verificar upload_max_filesize. Ao invés de procurar o arquivo, eu fui no ícone do WampServe, e lá encontrei a mesma informação em PHP > PHP Settings, e o meu estava configurado para 2M. Alterei para 4M. Agora sim eu vou poder trabalhar com o ZipArchive.
  5. O mês de agosto terminou e eu pedi para a Bling todas as notas que o meu irmão emitiu. A Bling disse que foram emitidas 635 arquivos. Depois de descompactar o arquivo da Bling, o Windows disse que ele encontrou 637 arquivos. E finalmente o scandir do PHP disse que encontrou 639 arquivos. Esse desencontro de informações é um problema sério e vem desde o tempo de Moisés, onde o Arquiteto diz isso e a criatura entende aquilo. Isso não podia ser diferente com a Bling, o Windows e o PHP, todos eles são imagem e semelhança de nossos desencontros. Para começar a apuração, eu pedi para o PHP listar todas as notas que ele encontrar na pasta. Tem duas notas que ele não conseguiu listar. Esses arquivos terminam com a extensão can.xml ao invés de nfe.xml, e assim eu aprendi que cancelar uma nota significa CRIAR um novo arquivo e não simplesmente ACABAR com o arquivo original. Não dá para ver uma nota cancelada pelo Windows, mas dá para ver com o PHP: <?php // index.php $path="C:/Users/Frank/Downloads/xml_nfce_1725380458"; $arquivos=scandir($path); foreach($arquivos as $arquivo): echo "<table>"; if($arquivo!=="." && $arquivo!==".."): $xml=simplexml_load_file($path."/".$arquivo); if($xml->NFe->infNFe): echo "<tr><td>".$xml->NFe->infNFe->ide->nNF."</tr>"; else: var_dump($xml); endif; endif; endforeach;
  6. Tentei intercalar o dropdown do Bootstrap no meio dos marcadores do Blade, e nada deu certo. A minha saída foi me livrar dos marcadores do Blade: resources.views.layouts.navigation.blade.php <nav x-data="{ open: false }" class="bg-white border-bottom border-gray-300"> <table> <td> <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" style=margin-left:120px> Dashboard </x-nav-link> <td> <x-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')" style=margin-left:120px> Chirps </x-nav-link> <td class="text-end" style=width:1000px> <div class=dropdown> <button class="btn btn-sm btn-outline-secondary d-inline-flex align-items-center dropdown-toggle" data-bs-toggle=dropdown> {{ Auth::user()->name }} </button> <div class=dropdown-menu> <x-dropdown-link :href="route('profile.edit')"> Profile </x-dropdown-link> <form method="POST" action="{{ route('logout') }}"> @csrf <x-dropdown-link :href="route('logout')" onclick="event.preventDefault();this.closest('form').submit();"> Log Out </x-dropdown-link> </form> </div> </div> </table>
  7. O meu conhecimento em HTML é bastante precário, ainda não sei mexer no CSS e no elemento <div>. Hoje estudei um pouco o Chirp do Laravel, desativei o vite e no lugar coloquei o CDN do Bootstrap. Tentei encontrar o equivalente do Tailwinds no Bootstrap com a Gemini, mas isso não deu certo. Então resolvi apagar todas as <div> que havia no código blade, e no lugar eu coloquei um <table> e três <td>. Cada célula ficou com o elemento <x>, o novo marcador do Blade. Com a ajuda da Gemini, descobri que o marcador <x> do Blade suporta o CSS do Bootstrap ou um particular como style=margin-left:120px. Isso deu certo no marcador <x-nav-link>, mas não deu certo no marcador <x-dropdown>. A minha saída foi mexer no marcador <td>. Pedi para aumentar a célula para 1000px e alinhar tudo na direita assim <td class="text-end" style=width:1000px>. Certamente, trata-se de uma gambiarra. O código que segue resolve o problema do alinhamento dos elementos na barra de navegação, ainda falta resolver o problema do dropdown, preciso ver como o Bootstrap consegue exibir e ocultar as opções de um menu: arquivo resources.views.layouts.navigation.blade.php <nav x-data="{ open: false }" class="bg-white border-bottom border-gray-300"> <table> <td> <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" style=margin-left:120px> Dashboard </x-nav-link> <td> <x-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')" style=margin-left:120px> Chirps </x-nav-link> <td class="text-end" style=width:1000px> <x-dropdown> <x-slot name="trigger"> <button class="btn btn-sm btn-outline-secondary d-inline-flex align-items-center"> {{ Auth::user()->name }} </button> </x-slot> <x-slot name="content"> <x-dropdown-link :href="route('profile.edit')"> Profile </x-dropdown-link> <form method="POST" action="{{ route('logout') }}"> @csrf <x-dropdown-link :href="route('logout')" onclick="event.preventDefault();this.closest('form').submit();"> Log Out </x-dropdown-link> </form> </x-slot> </x-dropdown> </table>
  8. Testei os dois códigos, e não encontrei nenhum erro. O problema é que fiz uma adaptação para fazer os códigos funcionarem, seria necessário todos os códigos para ver exatamente o que acontece. O mais importante é incluir as mensagens de erro, coisa que você não fez. Mas se você suspeita que a função session_start( ) está bloqueando o acesso ao banco de dados, tente isso: <?php // index.php session_start(); $conn=new mysqli("localhost","root","","terminais"); $teste=$conn->query("select * from users"); var_dump($teste); As adaptações que eu fiz para fazer o seu código funcionar estão aqui: <?php // index.php // include('db.php'); mysql users(id,username,password); mysql tasks(id,user_id) session_start(); $conn=new mysqli("localhost","root","","terminais"); // $hashed=password_hash('1234',PASSWORD_BCRYPT); // $conn->query("insert into users (username,password) values ('frank','$hashed')"); $username = 'frank'; // $_POST['username']; $password = '1234'; // $_POST['password']; // Check credentials $sql = "SELECT * FROM users WHERE username = '$username'"; $result = mysqli_query($conn, $sql); $user = mysqli_fetch_assoc($result); if (password_verify($password, $user['password'])) { $_SESSION['user_id'] = $user['id']; // header('Location: tasks.php'); } else { echo "Invalid credentials!"; } $user_id = $_SESSION['user_id']; $sql = "SELECT * FROM tasks WHERE user_id = '$user_id'"; $result = mysqli_query($conn, $sql); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="styles.css"> <title>Task List</title> </head> <body> <h1>Task List</h1> <div class="task-container"> <?php while($task = mysqli_fetch_assoc($result)): ?> <div class="task"> <h2><?php echo $task['title']; ?></h2> <p><?php echo $task['description']; ?></p> <p>Start: <?php echo $task['start_date']; ?> | End: <?php echo $task['end_date']; ?></p> <p>Author: <?php echo $task['author']; ?></p> <a href="edit_task.php?id=<?php echo $task['id']; ?>">Edit</a> <a href="delete_task.php?id=<?php echo $task['id']; ?>">Delete</a> </div> <?php endwhile; ?> </div> <a href="new_task.php">Create New Task</a> </body> </html>
  9. Além da Gemini, eu também comecei a utilizar o Copilot, tudo na esperança de melhorar o meu código PHP. Nessa semana eu encontrei o Iterables em https://www.w3schools.com/php/php_iterables.asp - tentei testar o código, mas o VS Code reclamou que o método MyIterator::current( ) não é compatível com o método Iterator::current( ). Pedi ajuda para a Gemini e o Copilot, e ambos forneceram o mesmo código, e pediram desculpas por não ajudar mais. A minha tese é de que o PHP foi abandonado, ninguém mais quer saber dessa linguagem. Logo, eu não vou mais perder tempo procurando ajuda onde não existe. Eu tenho conta no iMasters, mas eu preciso passar pela rotina da verificação em duas etapas. Já pedi ajuda várias vezes para o Imasters, e eles falam que o código de ativação pelo Authenticator já foi encaminhado no meu email, só que ele nunca veio. Acredito que o fórum de debate do PHP também vai ser abandonado. E como eu não sei mexer na biblioteca do PHP, tudo o que me resta é seguir o mesmo caminho: esquecer o PHP, o MySQL, o HTML, o JavaScript, o Laravel, e tantos outros recursos que estão disponíveis na internet de graça, só que sem ninguém para explicar como fazer tudo isso funcionar. É uma pena. A outra alternativa é eu tentar continuar com o espírito da tentativa e erro. Quem sabe se eu consigo criar um código que consiga eliminar os mosquitos que teimam em hospedar nos vasos das plantas e flores. Pelo menos, o Copilot e a Gemini ainda não conseguem fazer esse tipo de serviço.
  10. Desde 2020 é que estudo o PHP e em 2022 comecei a brincar com o Laravel. O melhor brinquedo que eu encontrei nesse tempo todo foi o var_dump, ele é mais poderoso que o echo, pois ele consegue exibir na tela muito mais que uma string, como é o caso do registro de um banco de dados. object(stdClass) é um nome inventado pelo PHP para guardar informações do tipo registro do banco de dados. Ele tem várias camadas, assim object(stdClass)[0], object(stdClass)[1]... mas na hora de usar o var_dump, ele só mostra o quinto "atributo"; Muito mais bacana que o var_dump( ) é o dd( ) do Laravel, ele é fantástico. O dd( ) mostra uma montanha de informações a respeito de uma variável, além de parar a execução do programa. O único problema é que ele só funciona no Laravel. Nessa nova pesquisa, eu acabei encontrando um método do PDO chamado fetchObject( ), e ele tem a mesma funcionalidade do fetch_assoc( ) do mysqli, ele só mostra o primeiro registro que encontrar pelo caminho, e não mais que isso. Para testar isso, usei o seguinte código: <body style="width:500px;margin:0 auto;margin-top:50px"> <?php class Conn { private static $instancia; private $pdo; private function __construct() { $this->pdo=new PDO("mysql:host=localhost;dbname=diario","root",""); } static function instancia() { if(!self::$instancia) { self::$instancia=new self(); } return self::$instancia; } function select($sql,$params=[]) { $stmt=$this->pdo->prepare("select $sql"); for($i=0;$i<count($params);$i++) { $stmt->bindParam($i+1,$params[$i]); } $stmt->execute(); return $stmt->fetchObject(); // alterar para fetchAll(PDO::FETCH_OBJ) } } $codprod=2; $produto=Conn::instancia()->select("* from tbprod where codprod > ? limit 2",[$codprod]); var_dump($produto); ?> </body> resultado: C:\wamp64\www\Astudy\index.php:36: object(stdClass)[4] public 'codprod' => int 3 public 'un' => string 'pc' (length=2) public 'prod' => string 'Chave Fenda Teste Rayco' (length=23) public 'custo' => string '2.50' (length=4) public 'marg' => float 30 public 'codbar' => string '7898338092162' (length=13) public 'loc' => string '4B6b' (length=4) public 'emb' => float 12 public 'cf' => string '82054000' (length=8) public 'codforn' => string 'e380482' (length=7) public 'estoque' => float 12 public 'origem' => int 0 public 'vazio' => null public 'venda' => string '3.50' (length=4) Os quatro últimos campos desse registro violam o principio básico do banco de dados, ninguém calcula o estoque dentro da tabela de produtos ou coloca o preço de venda lá ou cria um campo para não ter nenhuma informação como o 'vazio'. No meu caso, precisei fazer essa gambiarra para poder comparar o meu banco de dados com o banco de dados da Bling que montei a partir das planilhas que eles fornecem.
  11. Eu ainda não consegui entender a função estática, ele é um conceito muito abstrato. Pelo que vi nos tutoriais, a função estática permite usar a classe ao invés de usar um objeto (uma espécie de cópia da classe com atributos particulares). Com a ajuda da Gemini consegui montar o código, entender que é o problema, eu ainda não sei o que é "injeção de SQL" e não sei como o PDO pode evitá-lo. <?php class Conn { private static $instancia; private $pdo; private function __construct() { $this->pdo=new PDO("mysql:host=localhost;dbname=diario","root",""); } static function instancia() { if(!self::$instancia) { self::$instancia=new self(); } return self::$instancia; } function select($sql,$params=[]) { $stmt=$this->pdo->prepare("select $sql"); for($i=0;$i<count($params);$i++) { $stmt->bindParam($i+1,$params[$i]); } $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_OBJ); } } $codprod=1; $produto=Conn::instancia()->select("* from tbprod where codprod = ?",[$codprod]); var_dump($produto);
  12. Hoje estudei o <input list> e achei muito bacana. Para procurar um produto, basta digitar algumas letras, não importa se ele está no começo, no meio ou no fim da descrição: <?php $prod=[['Maçã Tamanho Grande'=>10],['Maçã amanho Médio'=>5], ['Maçã Tamanho Pequeno'=>2]]; ?> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> form {display:flex; justify-content:center; align-items:center; height: 10vh} </style> <form> <input list=lsproduto name=produto placeholder=produto onchange=submit() autofocus autocomplete=off> <datalist id=lsproduto> <?php foreach($prod as $key => $vetor): foreach($vetor as $fruta => $preço) : ?> <option value='<?=$fruta?> :<?=$preço?>'> <?php endforeach; endforeach;?> </form>
  13. Encontrei um recurso bacana no VS Code que permite localizar um texto em vários arquivos. Por exemplo, na classe Pessoa eu tinha uma função chamada lista, decidi mudar para inicio. Pedi para o VS Code localizar todos os Pessoa.lista nos arquivos e mudar para Pessoa.inicio. Estou começando a padronizar. Aonde estava public function, decidi mudar tudo para function. Na classe Produto eu tinha a função procurar e a função consultar, mudei para filtra e consulta, ou seja, ao invés de usar o infinitivo vou tentar usar o indicativo da 3a. pessoa do singular. Agora, para pular de um formulário 1 para formulário 2, e de lá ir para o formulário 3 e depois voltar para o formulário 1 não vi outra solução senão apelando para o $_SESSION['end'], assim: arquivo orcamento.php (listagem parcial) <?php class Orcamento { function pessoa($pedido) { $_SESSION['end']="?Orcamento.pessoaSelecionada.$pedido"; return header("location:?Pessoa.inicio"); } function pessoaSelecionada($pedido) { $codp=$_SESSION['codp']; (new Conn)->update("tbpedido set codp = $codp where ped=$pedido"); return $this->inicio($pedido); } } É óbvio que usar variável global é perigoso, principalmente quando ele é utilizado como uma rota (tipo if(isset($_SESSION['end']){ } else { }), eu já vi muitos resultados inesperados aqui, assim sempre dou um jeito de reinicializar a variável global (unset($_SESSION['end'])) quando achar oportuno. Certamente é um péssimo exemplo de programação. Mas é para isso que serve o fórum, alguém pode sugerir coisa melhor.
  14. Eu não conheço o serviço do LimeSurvey, mas achei curioso parte do código que você publicou. Ele tem bastante private function, e assim eu não consigo testar tipo (new teste)->updateQuestionsOptions(1,["José","Maria"]). Pelo que eu apurei, só existe uma tabela chamada questions com os campos qid, options,sid,title, sem alguns exemplos é difícil imaginar como usar essa tabela. O mais estranho é a função update que termina indo para a função set, são dois serviços diferentes e não poderiam ser misturados. Tudo me leva a crer que você está perdido. O mais importante é fazer um projeto mais simples possível. Se você quer expulsar o seu chefe da sua sessão é só fazer um abaixo assinado com os colegas e entregar para o patrão, o LimeSurvey, o MySQL, o JavaScript e o PHP só vão atrasar você.
  15. A mensagem do William é importante. Não dá para usar o Tailwinds como o Bootstrap. Tentei usar o JavaScript para exibir as opções do menu, mas o inspetor do navegador disse a mesma coisa que o William, o cdn é para testes rápidos, nada tão sofisticado como exibir opções. É uma pena, isso vai dificultar bastante o estudo do Laravel.
  16. Com o velho método da tentativa e erro e a Gemini consegui imitar mais ou menos o formulário do login do Bootstratp: arquivo appView.php <!DOCTYPE html> <html lang="en"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://cdn.tailwindcss.com"></script> <title>Projeto Classe</title> <body class="md:w-4/12 md:mx-auto bg-gray-200"> arquivo loginView.php <?php require 'appView.php'; ?> <main class="h-screen flex justify-center items-center"> <?php if(isset($_SESSION['mensagem'])): ?> <div> <?=$_SESSION['mensagem']?> </div> <?php endif; ?> <form method=post action="?login.menu"> <h1 class="text-2xl mb-4 font-normal">Projeto Classe</h1> <div class="relative"> <input type="email" name=email class="border px-4 py-2 rounded-md focus:outline-none py-3"> <label class="absolute left-4 -top-0 text-gray-500 text-xs">Email</label> </div> <div class="mb-1"></div> <div class="relative"> <input type="password" name=senha class="border px-4 py-2 rounded-md focus:outline-none py-3"> <label class="absolute left-4 -top-0 text-gray-500 text-xs">Senha</label> <div class="mb-1"></div> <button class="w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded" type="submit">Entrar</button> </form> </main>
  17. Hoje fui ver o código Laravel do welcome.blade.php e para a minha surpresa encontrei um link para o cdn do Tailwinds, do mesmo jeito que eu faço com o Bootstrap. O Bootstrap tem dois links, um para o CSS e outro para o JavaScript, já o Tailwinds só tem o link para o JavaScript. A primeira conclusão que eu cheguei é que agora eu posso usar o Tailwinds no PHP, ou que não preciso usar o composer para ajeitar as classes CSS no Laravel. Fui no meu laboratório wamp64.www.astudy e criei o famoso arquivo index.php: <script src="https://cdn.tailwindcss.com"></script> <div class="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4"> <div class="shrink-0"> <img class="size-12" src="laravel.svg"> </div> <div> <div class="text-xl font-medium text-black">Frankbook</div> <p class="text-slate-500">Você tem uma nova mensagem!</p> </div> </div> Agora sim eu vi que é negócio trabalhar com o Tailwinds, ele é tão versátil quanto o Bootstrap. O problema é conseguir o arquivo de imagem .svg - pesquisando na internet encontrei um portal que pediu R$ 40,00 por mês. Eu fui no Wikipedia e lá encontrei um .svg com o logo do Laravel. Baixei no PC e utilizei no código de teste. Mas o Wikipedia me alertou que posso ir na cadeia se o autor da imagem reclamar que estou usando a imagem sem o consentimento dele. Por esse motivo, o meu projeto Orçamento não tem nenhuma imagem para não ter dor de cabeça com o TSJ.
  18. Em 2022, eu não sabia como criar um método para cada tarefa. A minha saída foi criar apenas um método para executar várias tarefas. Basicamente eu copiei o código que eu tinha no PHP e fui colando na pasta view do Laravel. Aqui em 2024, consegui resolver esse problema. Basicamente todo formulário HTML tem apenas quatro tarefas: incluir, excluir, alterar e consultar. Para diferenciar as tarefas do formulário Fornecedor e do Produto, inclui o nome da classe junto com a tarefa, assim: produto.incluir, produto.excluir, etc. O meu primeiro código ficou assim: arquivo app.Http.Controllers.Etiqueta.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\tbprod; class Etiqueta extends Controle { public function inicio($produto=null,$venda=null) { if(session()->has('etiqueta')) { $etiquetas=session('etiqueta'); } else { $etiquetas=[]; session(['etiqueta'=>$etiquetas]); } return view('etiqueta',['produto'=>$produto,'venda'=>$venda,'etiquetas'=>$etiquetas]); } public function procurar(Request $request) { $procurar=str_replace(" ","%",$request->input('procurar')); session(['end'=>"etiqueta.titulo"]); session(['filtro'=>$procurar]); return redirect('produto'); } public function apagar() { session()->forget('etiqueta'); $etiquetas=[]; return $this->inicio(); } public function etiquetar(Request $request) { $titulo=$request->input('titulo'); $venda=$request->input('venda'); $etiquetas=session('etiqueta'); $etiquetas[]=(object)['titulo'=>substr($titulo,0,12),'venda'=>$venda]; session(['etiqueta'=>$etiquetas]); session()->forget('codprod'); return $this->inicio(); } public function titulo() { $codprod=session('codprod'); $prod=tbprod::where('codprod',$codprod)->first(); $produto=$prod->prod; $venda=$prod->venda; return $this->inicio($produto,$venda); } } arquivo routes.web.php <?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\Etiqueta; Route::middleware(['auth','verified'])->group(function() { Route::get('etiqueta',[Etiqueta::class,'inicio']); Route::get('etiqueta.apagar',[Etiqueta::class,'apagar']); Route::get('etiqueta.etiquetar',[Etiqueta::class,'etiquetar']); Route::get('etiqueta.procurar',[Etiqueta::class,'procurar']); Route::get('etiqueta.titulo',[Etiqueta::class,'titulo']); } arquivo resources.views.etiqueta.blade.php @include('menu') <script> btmenu.innerHTML="Etiquetas" document.title="Etiquetas" function imprimir() { cabecalho.style='display:none' esconder.style='display:none' window.print() location.replace('etiqueta') } </script> <div id=esconder> <table><tr><td> <form action='etiqueta.procurar'> Pesquisar <input placeholder=produto name=procurar onchange=submit()> </form> <td class=px-3><a href=etiqueta.apagar>Apagar</a> <td class=px-3><a href=# onclick=imprimir()>Imprimir</a> </table> <p> <form action='etiqueta.etiquetar'> <input name=titulo value='{{$produto}}' size=40><br> <input name=venda value='R$ {{dec($venda)}}'><br> <input type=submit value='Incluir na lista de Etiquetas'> </form> </p> </div> <table class=table-bordered> @foreach($etiquetas as $key=>$etiqueta) @if(($key) % 5==0) <tr class=fw-bold> @endif <td style="padding: 1rem"> <div> {{$etiqueta->titulo}} </div> <div> {{$etiqueta->venda}} </div> @endforeach </table>
  19. Laravel é um impressionante programa que estou tentando aprender desde 2022. Eu só tenho o módulo básico, mas consegui encontrar na internet uma rotina para fazer login. Hoje eu vi que o VS Code reclamou que o código de login tem erro, e com a ajuda da Gemini eu consegui consertar: arquivo app.Http.Controllers.CustomAuthController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use App\Models\User; use Illuminate\Support\Facades\Session; class CustomAuthController extends Controle { public function alterarSenha(request $request) { if($request->input('password')) { $senha=$request->input('password'); User::where('id',Auth::user()->id)->update(['senha'=>Hash::make($senha)]); return $this->signOut(); } $email=Auth::user()->email; return view('auth.alterarSenha',['email'=>$email]); } public function index() { return view('auth.login'); } public function customLogin(Request $request) { $request->validate(['email' => 'required','password' => 'required', ]); $credentials = $request->only('email', 'password'); if (Auth::attempt($credentials)) { return redirect()->intended('dashboard')->withSuccess('Signed in'); } return redirect()->back()->with('mensagem','Dados inválidos!'); } public function registration() { return view('auth.registration'); } public function customRegistration(Request $request) { $request->validate(['name' => 'required','email' => 'required|email|unique:users', 'password' => 'required|min:6',]); $data = $request->all(); $check = $this->create($data); return redirect("dashboard")->withSuccess('You have signed-in'); } public function create(array $data) { return User::create(['name' => $data['name'], 'email' => $data['email'],'password' => Hash::make($data['password'])]); } public function dashboard() { if(Auth::check()) { return view('menu'); } return redirect("login")->withSuccess('You are not allowed to access'); } public function signOut() { Session::flush(); Auth::logout(); return Redirect('login'); } } Depois que a Gemini me ajudou a consertar o problema, eu não consegui mais logar no programa. Foi aí que eu lembrei que mudei um monte de coisa, eu não tenho uma tabela User e sim tbusuarios, onde o nome do campo é senha e não password. Comentei isso com a Gemini, e mais uma vez ela ajudou a consertar o problema: arquivo app.Models.User.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; protected $table="tbusuarios"; protected $fillable = ['nome','email','senha',]; protected $hidden = ['senha', ]; public function getAuthPassword() { return $this->senha; } } Graças à Gemini, consegui logar novamente no Laravel. O Laravel tem recursos para deixar mais turbinado, mas o meu problema é o CSS Tailwinds, eu achei bem difícil trabalhar com ele. Agora que eu sei um pouco mais sobre classe pretendo melhorar o código no Laravel, ele está cheio de gambiarra.
  20. Ontem, eu criei um programa num ambiente que não tem classe, autoload e roteador. Fiquei imaginando como colocar o programa etiqueta.php num ambiente de classe. Para acessar o programa, acrescentei uma linha no programa menuView.php: <li><a class=dropdown-item href=etiqueta.php>Etiquetas</a></li> Para garantir o acesso ao banco de dados e várias funções personalizadas, usei: include('config.php'); Finalmente, para acessar o roteador e usar uma função particular da classe Produto, usei: if(isset($_GET['procurar'])) { $procurar=str_replace(" ","%",$_GET['procurar']); $_SESSION['end']="etiqueta.php"; $_SESSION['criterio']="where prod like '%$procurar%'"; header("location:./?Produto.produto"); } A variável $_SESSION['end'] vai garantir que o comando voltará para a etiqueta.php assim que o objeto Produto terminar o serviço. O código final ficou quase a mesma coisa, esse é um típico programa hibrido, onde o PHP e o HTML compartilham o mesmo arquivo ao mesmo tempo. O certo é transformar a etiqueta num objeto, mas no momento eu não tenho a menor ideia de como fazer isso. <?php include('config.php'); include('menuView.php'); $_SESSION['etiqueta']=(isset($_SESSION['etiqueta'])) ? $_SESSION['etiqueta'] : []; $produto=""; $venda=""; if(isset($_GET['procurar'])) { $procurar=str_replace(" ","%",$_GET['procurar']); $_SESSION['end']="etiqueta.php"; $_SESSION['criterio']="where prod like '%$procurar%'"; header("location:./?Produto.produto"); } if(isset($_GET['apagar'])) { unset($_SESSION['etiqueta']); unset($_SESSION['codprod']); header("location:etiqueta.php"); } if(isset($_GET['titulo'])) { $titulo=$_GET['titulo']; $venda=$_GET['venda']; $_SESSION['etiqueta'][]=(object)['titulo'=>$titulo,'venda'=>$venda]; unset($_SESSION['codprod']); header('location:etiqueta.php'); } if(isset($_SESSION['codprod'])) { $codprod=$_SESSION['codprod']; $prod=(new Conn)->select("* from tbprod where codprod=$codprod")[0]; $produto=$prod->prod; $venda=$prod->venda; } ?> <script> btmenu.innerHTML="Etiquetas" document.title="Etiquetas" function imprimir() { cabecalho.style='display:none' esconder.style='display:none' window.print(); } </script> <div id=esconder> <table><tr><td> <form> Pesquisar <input placeholder=produto name=procurar onchange=submit()> </form> <td><a href=etiqueta.php?apagar>Apagar</a> <td><a href=etiqueta.php?imprimir onclick=imprimir()>Imprimir</a> </table> <p> <form> <input name=titulo value='<?=$produto?>' size=40><br> <input name=venda value='R$ <?=dec($venda)?>'><br> <input type=submit value='Incluir na lista de Etiquetas'> </form> </p> </div> <table class=table-bordered> <?php $etiquetas=$_SESSION['etiqueta']; foreach($etiquetas as $key=>$etiqueta): if(($key) % 5==0):?> <tr class=fw-bold> <?php endif; ?> <td style="padding: 1rem"> <div> <?=$etiqueta->titulo?> </div> <div> <?=$etiqueta->venda?> </div> <?php endforeach; ?> </table>
  21. Há cerca de 20 anos, eu usei o PowerPoint em conjunto com o Access para imprimir etiquetas. Comecei a estudar o PHP em 2020, mas só hoje consegui montar um programa para imprimir etiqueta: aruivo etiqueta.php <?php include('menu.php'); $_SESSION['etiqueta']=(isset($_SESSION['etiqueta'])) ? $_SESSION['etiqueta'] : []; $produto=""; $venda=""; if(isset($_GET['apagar'])) { unset($_SESSION['etiqueta']); header("location:etiqueta.php"); } if(isset($_GET['titulo'])) { $titulo=$_GET['titulo']; $venda=$_GET['venda']; $_SESSION['etiqueta'][]=(object)['titulo'=>$titulo,'venda'=>$venda]; unset($_SESSION['codprod']); header('location:etiqueta.php'); } $_SESSION['end']="etiqueta.php"; if(isset($_SESSION['codprod'])) { $codprod=$_SESSION['codprod']; $prod=$mysqli->query("select * from tbprod where codprod=$codprod") ->fetch_assoc(); $produto=$prod['prod']; $venda=$prod['venda']; } ?> <script> btmenu.innerHTML="Etiquetas" document.title="Etiquetas" function imprimir() { cabecalho.style='display:none' esconder.style='display:none' window.print(); } </script> <div id=esconder> <table><tr><td> <form action=produto.php> Pesquisar <input placeholder=produto name=procurar onchange=submit()> </form> <td><a href=etiqueta.php?apagar>Apagar</a> <td><a href=etiqueta.php?imprimir onclick=imprimir()>Imprimir</a> </table> <p> <form> <input name=titulo value='<?=$produto?>' size=40><br> <input name=venda value='R$ <?=dec($venda)?>'><br> <input type=submit value='Incluir na lista de Etiquetas'> </form> </p> </div> <table class=table-bordered> <?php $etiquetas=$_SESSION['etiqueta']; foreach($etiquetas as $key=>$etiqueta): if(($key) % 5==0):?> <tr class=fw-bold> <?php endif; ?> <td style="padding: 1rem"> <div> <?=$etiqueta->titulo?> </div> <div> <?=$etiqueta->venda?> </div> <?php endforeach; ?> </table>
  22. Eu usei letra minúscula para todos os arquivos e letra maiúscula para todas as classes. Isso deu certo aqui no Windows. Mas na hora de hospedar o código PHP no Hostinger, eu só tive problema. Eu pensei que o autoload carregava a classe, mas olhando o código eu vi que ele carrega o arquivo que contém a classe e não a classe. No Linux, o tamanho da letra é importante quando o assunto é arquivo. Como todo programador iniciante, eu fiquei apavorado. Mas usando o princípio da tentativa e erro, pensei em mudar o autoload ao invés de editar todos os códigos, e deu certo: arquivo config.php (listagem parcial) <?php spl_autoload_register(fn ($class) => require str_replace('\\', DIRECTORY_SEPARATOR, strtolower($class)) . '.php');
  23. Se você tiver um código mais ou menos parecido com isso <?php $mysqli=new mysqli("localhost","root","","diario"); if (isset($_POST['custo'])) { $custo = $custo=str_replace(",",".",$_POST['custo']); $mysqli->query("update tbhistprod set custototal=$custo where id=1"); echo $mysqli->query("select custototal from tbhistprod where id=1") ->fetch_assoc()['custototal']; } ?> <form method='post'> <table> <tr><td>Custo Total<td><input name='custo' onchange='submit()'> </table> </form> e se você precisar de uma calculadora, você pode usar a caixa do <input>. Se você escrever "12*12" na caixa do <input>, o PHP vai fazer o cálculo para você. Isso parece mágica, mas não é. Onde está escrito 'set custototal=$custo' o PHP traduz assim 'set custototal=12*12'. Já o Laravel trabalha com o Eloquent, e o Eloquent trunca parte da equação sem resolver coisa alguma. Mas com a ajuda da Gemini, eu tive a seguinte dica: <?php class DiarioController extends Controller { public function detalhe(Request $resquest) { if($request->input('qt')) { $qt=fmt($request->input('qt')); $custo1=fmt($request->input('custo')); $custo=eval("return $custo1;"); $lcto=session('lcto'); $docto=session('docto'); $codp=session('codp'); $codprod=session('codprod'); $dia=tbdiario::where('lcto',$lcto)->value('dia'); tbhistprod::insert(['lcto'=>$lcto,'codp'=>$codp,'dia'=>$dia, 'codprod'=>$codprod,'qt'=>$qt,'custototal'=>$custo]); } }
  24. Hoje eu tentei listar os dez primeiros produtos, mas não estava conseguindo. Verifiquei o código e não consegui encontrar o problema. Depois de duas horas eu vi que o problema estava na variável $_SESSION. Para resolver o problema, alterei o menu de <a href=?Produto.lista> para <a href=?Produto.lista&apagarCriterio>. A seguir um rascunho que fiz no index.php: <table> <?php session_start(); class Conn { private static $pdo; public static function instancia() { if(!self::$pdo) { self::$pdo=new PDO("mysql:host=localhost;dbname=diario","root",""); } return self::$pdo; } function select($sql) { $stmt=$this->instancia()->query("select $sql"); return $stmt->fetchAll(PDO::FETCH_OBJ); } } if(isset($_GET['recomecar'])) { unset($_SESSION['criterio']); } $_SESSION['criterio']=(isset($_SESSION['criterio'])) ? $_SESSION['criterio'] : null; $criterio=$_SESSION['criterio']; $produtos=(new Conn)->select("* from tbprod where prod >= '$criterio' order by prod"); $i=0; foreach($produtos as $prod): $i++; ?> <tr><td><?=$prod->codprod?><td><?=$prod->prod?> <?php if($i==10) { ?> </table> <form><input type=submit value=Continuar onclick=submit()></form> <form><input type=submit value=Recomeçar name=recomecar onclick=submit()></form> <?php $_SESSION['criterio']=$prod->prod; break; } endforeach;
  25. O resultado que o PHP passa para o valor acima é "verdadeiro", quando a resposta correta é "falso". Essa é uma dor de cabeça na hora de procurar diferença entre o valor contábil e o valor do estoque. Outro problema é quando você faz um lançamento contábil errado, tipo debitar estoque e creditar custo do estoque na hora de baixar o estoque, ou fazer o lançamento contábil correto, mas um dos itens do estoque você lança com uma diferença de R$ 0,10. Para o primeiro problema, eu resolvi assim: echo ((4.5-4.5)>0.001) ? "verdadeiro" : "falso"; Para os demais, eu resolvi assim: arquivo estoque.php (listagem parcial) class Estoque { public function diferencaEstoque() { $centrada = (new Conn)->select("sum(valor) as soma from tbdiario where contad=123")[0]->soma; $cestoque = (new Conn)->select("sum(custototal) as soma from tbprod join tbhistprod on tbprod.codprod=tbhistprod.codprod where custototal>0 and loc<> 'a24'")[0]->soma; $dias = (new Conn)->select("dia,sum(custototal) as soma from tbprod join tbhistprod on tbprod.codprod=tbhistprod.codprod where custototal>0 and loc<>'a24' group by dia order by dia"); $contabeis=(new Conn)->select("dia from tbdiario where contad=123 group by dia"); $diasContabeis=count($contabeis); $diasEstoque=count($dias); $vetores = []; foreach($contabeis as $dia) { $data=$dia->dia; if(!(new Conn)->select("dia from tbhistprod where dia='$data' and custototal > 0")) { $valor = (new Conn)->select("dia, sum(valor) as soma from tbdiario where contad=123 and dia='$data'")[0]->soma; $vetores[] = (object)['data' => $data, 'entrada' => null, 'valor' => $valor, 'diferenca' => $valor]; } } foreach ($dias as $dia) { $data = $dia->dia; $entrada = $dia->soma; $valor = (new Conn)->select("dia, sum(valor) as valor from tbdiario where contad=123 and dia='$data'")[0]->valor; $diferenca = $valor - $entrada; if($diferenca>0.001) { $vetores[] = (object)['data' => $data, 'entrada' => $entrada, 'valor' => $valor, 'diferenca' => $diferenca]; } } return view('estoqueDiferenca', ['centrada' => $centrada,'cestoque' => $cestoque, 'vetores' => $vetores, 'diasContabeis'=>$diasContabeis, 'diasEstoque'=>$diasEstoque]); } } arquivo estoqueDiferenca.php <?php include "menuView.php"; ?> <table class='table table-striped linha'> <tr><td>Contábil<td class=text-end><?=dec($centrada)?> <td>Dias Contábeis<td class=text-end><?=$diasContabeis?> <tr><td>Estoque<td class=text-end><?=dec($cestoque)?> <td>Dias Estoque<td class=text-end><?=$diasEstoque?> <tr class=fw-semibold><td>Dia<td class=text-end>Contábil<td class=text-end>Estoque <td class=text-end>Diferença <?php foreach($vetores as $v): ?> <tr><td><?=fmt($v->data)?> <td class='text-end'><?=dec($v->valor)?> <td class='text-end'><?=dec($v->entrada)?> <td class='text-end'><?=dec($v->diferenca)?> <?php endforeach;
×
×
  • Criar Novo...