Noticias php+Mysql y PDO

Amigos foreros,

Encontré este script navegando por ahí y por allá y luego de modificar algunas cosas, en especial el método de login, funciona como relojito. Solo que no he resuelto el asunto de editar y eliminar los registros. Eso lo dejo en manos de los que son más expertos y entendidos en el POO y PDO. Ah, tiene formulario de comentarios.

En realidad, es un script muy sencillo, pero bastante poderoso y, obviamente, es perfectible.

Me esforcé por pegar acá los respectivos códigos, pero no lucía bien el asunto. Así es que he subido todo a DropBox. Descarguen la aplicación y pruébenla.

Repito, no está terminado, falta lo de la editar y eliminar registros. Por favor, ruego que alguien colabore. Gracias

El enlace de descarga: https://dl.dropboxusercontent.com/u/97286094/noticiasPDO.rar

Saludos!
Por favor, ¿y pueden ayudarme con esto? En la misma aplicación, quiero subir y almacenar el nombre de la foto, con este formulario:.... por cierto, la imagen sube sin problemas, a la carpeta seleccionada, pero no se almacena el nombre de a misma en la base de datos MySQL.

<?php
session_start(); 
require('config/index.php');
if(isset($_SESSION['username'])): 
    if(isset($_POST['news'])): 
        if(empty($_POST['title']) || empty($_POST['content'])):
        echo 'Hay campos en blanco'; 
        else: 
            $title = $connection->prepare("SELECT title FROM news WHERE title = :title"); 
            $title->bindParam(':title',$_POST['title']); 
            $title->execute(); 
            if($title->fetchColumn() == $_POST['title']): 
                echo 'Existe una noticia con el mismo titulo'; 
            else: 
                $news = $connection->prepare("INSERT INTO news(id,user,title,content,foto,date,ip) 
                VALUES ('', '".$_SESSION['username']."', :title, :content, :foto,'".date('H:i:s d/m/Y')."', :ip)"); 
                $news->bindParam(':title',$_POST['title']); 
                $news->bindParam(':content',$_POST['content']);
                $news->bindParam(':foto',$_POST['foto']);
                $news->bindParam(':ip',$_SERVER['REMOTE_ADDR']);


    if(is_uploaded_file($_FILES['foto']['tmp_name'])) { // verifica haya sido cargado el archivo
    $ruta= "uploads/".$_FILES['foto']['name']; // Se guardaría dentro de "fotos" con el nombre original
    move_uploaded_file($_FILES['foto']['tmp_name'], $ruta);
     if(move_uploaded_file($_FILES['foto']['tmp_name'], $_FILES['foto']['name'])) { // se coloca en su lugar final
    echo "<b>Upload exitoso!. Datos:</b><br>";
    echo "Nombre: <i><a href=\"".$_FILES['foto']['name']."\">".$_FILES['foto']['name']."</a></i><br>";
    echo "Tipo MIME: <i>".$_FILES['foto']['type']."</i><br>";
    echo "Peso: <i>".$_FILES['foto']['size']." bytes</i><br>";
    echo "<br><hr><br>";
        }
    }


                $news->execute();
                echo 'Noticia creada correctamente';
                endif;
            endif;    
        endif;
?>
<form action="" method="post" enctype="multipart/form-data"> 
<input name="title" placeholder="Titulo de la noticia" size="120"><br>
<br>
<textarea name="content" placeholder="Contenido de la noticia" rows="10" cols="70"></textarea><br>
<br>
<input name="foto" type="file" id="foto" class="button" />
<br>
<br>
<input name="news" type="submit" value="Crear noticia"> 
</form>
<?php    
endif;
?>


Me sale este error en pantalla y no se cómo resolver el asunto:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'foto' cannot be null' in ......


Por favor, de veras agradeceré mucho la ayuda de ustedes Gracias desde ya.
Hola!

Claro que no funciona, puesto que estás utilizando $_POST['foto']. Siendo que foto es un archivo debes acceder a esta a través del arreglo $_FILES, por ende deberías asignar $_FILES['foto']['name'] al enlazar el parámetro: $news->bindParam(':foto',$_FILES['foto']['name']);.

Por si acaso, el error dice que le estás pasando algo inexistente a una columna que tiene la restricción NOT NULL.

Saludos!
He hecho tantas pruebas que ya no recuerdo bien, pero me parece que ya hice el arreglo y no pasó nada. Voy a probar, otra vez y te cuento. Gracias, amigo!
skaparate escribió:Hola!

Claro que no funciona, puesto que estás utilizando $_POST['foto']. Siendo que foto es un archivo debes acceder a esta a través del arreglo $_FILES, por ende deberías asignar $_FILES['foto']['name'] al enlazar el parámetro: $news->bindParam(':foto',$_FILES['foto']['name']);.

Por si acaso, el error dice que le estás pasando algo inexistente a una columna que tiene la restricción NOT NULL.

Saludos!

Ahí sí, todo bien!! Genial. Gracias, skaparate!
Ahora solo falta agregar el formulario de edición de las noticias y estamos al otro lado. Una vez que tenga todo resuelto, vuelvo a subir el comprimido con los nuevos documentos.

A todo esto y hablando de editar, te quiero molestar con la última solicitud, amigo skaparate. No quiero quitarte tiempo, pero a partir de este código estoy hace rato tratando de editar y actualizar los registros, pero no me resulta. ¿Me podrías dar una mano con el asunto?

admin.php

<?php session_start(); require('config/index.php');    
 $desde = @$_GET['pag'] * 10;     
$hasta = (@$_GET['pag'] * 10) + 10; 
    $news = $connection->prepare("SELECT id,title,content,date FROM news LIMIT $desde,$hasta");    
$limite = 210;    
$news->execute();    
 while($news1 = $news->fetch(PDO::FETCH_ASSOC)):        
 echo '<h3><a href="editar.php?id='.urlencode($news1['id']).'">'.$news1['title'].'</a></h3>';        
echo ''.substr($news1['content'], 0, $limite).' [...]<br /><br />';     
endwhile;          
$count_news = $connection->query("SELECT COUNT(*) title FROM news")->fetch(PDO::FETCH_ASSOC);     
for($i = 0; $i < round($count_news['title'] / 10 + 1); $i++):         
echo '<a href="admin.php?pag='.$i.'">'.$i.'</a>';     
endfor; 
?>

editar.php

<?phprequire('config/index.php');    
if (isset($_GET['id'])) {        
//$sql = ("SELECT * FROM news WHERE id = :id");        
$news = $connection->prepare("SELECT * FROM news WHERE id = :id");        
$news->bindParam(':title',$GET['title']);       
 $news->bindParam(':content',$GET['content']);        
//$news->execute();       
 while($news = $news->fetch(PDO::FETCH_ASSOC));       
 }
?>    

<form action="" method="post">     
<input name="title" placeholder="Titulo de la noticia" value="<?php echo $news['title'];?>" size="110"><br><br>     
<textarea name="content" placeholder="Contenido de la noticia" rows="10" cols="40"></textarea><br>     
<input name="news" type="submit" value="Crear noticia"> 
</form>

Reconozco que estoy completamente perdido en esto de editar y actualizar los registros con el PDO.

Desde ya, muchísimas gracias por tu valiosa ayuda y tiempo :)
OK, solucionado lo del formulario de edición de cada noticia. Queda así:

<?php
require('config/index.php');
$id = (int) (isset($_GET['id'])) ? $_GET['id'] : 0; //if ternario
if(($id == 0)) {
header("Location: " . $_SERVER['HTTP_REFERER']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$query = $connection->prepare("SELECT id,title,content,date FROM news WHERE id=:id");
$query->bindParam(':id', $id);
$query->execute();
$row = $query->fetch();
if (!$row) {
header("Location: " . $_SERVER['HTTP_REFERER']);
exit;
 }
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
$content = (isset($_POST['content'])) ? $_POST['content'] : '';
$title = (isset($_POST['title'])) ? $_POST['title'] : '';
$query = $connection->prepare("UPDATE news SET title = :title,content = :content WHERE id=:id");
$query->bindParam(':title', $title);
$query->bindParam(':content', $content);
$query->bindParam(':id', $id);
$query->execute();
header("Location: admin.php");
exit;
} else {
header("Location: " . $_SERVER['HTTP_REFERER']);
exit;
}
?>
<form action="" method="post"> 
<input name="title" placeholder="Titulo de la noticia" value="<?php echo $row['title'];?>" size="110"><br><br> 
<textarea name="content" placeholder="Contenido de la noticia" rows="10" cols="40"><?php echo $row['content'];?></textarea><br> 
<input name="news" type="submit" value="Crear noticia"> 
</form>
Estimado skaparate,

¿Me podrías ayudar con este formulario? Mis disculpas por fregar tanto con el asunto :rolleyes: Mira, el formulario funciona bien a la hora de editar y subir la foto que acompaña a la noticia. Pero hay un problema: subo la foto, actualizo la información, pero la foto antigua queda haciendo nada en el servidor. ¿Qué hacer para que la nueva imagen "pise" a la antigua cuando se está actualizando toda la noticia?

Este es el código:

<?php
require('config/index.php');
$id = (int) (isset($_GET['id'])) ? $_GET['id'] : 0; //if ternario
if(($id == 0)) {
    header("Location: " . $_SERVER['HTTP_REFERER']);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    $query = $connection->prepare("SELECT id,title,content,foto,date FROM news WHERE id=:id");
    $query->bindParam(':id', $id);
    $query->execute();
    $row = $query->fetch();
    if (!$row) {
        header("Location: " . $_SERVER['HTTP_REFERER']);
        exit;
    }
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $content = (isset($_POST['content'])) ? $_POST['content'] : '';
    $title = (isset($_POST['title'])) ? $_POST['title'] : '';
    $query = $connection->prepare("UPDATE news SET title = :title,content = :content,foto = :foto WHERE id=:id");
    $query->bindParam(':title', $title);
    $query->bindParam(':content', $content);
	$query->bindParam(':foto',$_FILES['foto']['name']);
    $query->bindParam(':id', $id);


	/********** Sube la foto *****************/
	if(is_uploaded_file($_FILES['foto']['tmp_name'])) { // verifica haya sido cargado el archivo
	$ruta= "uploads/".$_FILES['foto']['name']; // Se guardaría dentro de "fotos" con el nombre original
	move_uploaded_file($_FILES['foto']['tmp_name'], $ruta);
    if(move_uploaded_file($_FILES['foto']['tmp_name'], $_FILES['foto']['name'])) { // se coloca en su lugar final
	echo "<b>Upload exitoso!. Datos:</b><br>";
	echo "Nombre: <i><a href=\"".$_FILES['foto']['name']."\">".$_FILES['foto']['name']."</a></i><br>";
	echo "Tipo MIME: <i>".$_FILES['foto']['type']."</i><br>";
	echo "Peso: <i>".$_FILES['foto']['size']." bytes</i><br>";
	echo "<br><hr><br>";
        					}
					}
	/*********** Fin sube foto *************/


    $query->execute();
    header("Location: admin.php");
    exit;
} else {
    header("Location: " . $_SERVER['HTTP_REFERER']);
    exit;
}
?>
    <form action="" method="post" enctype="multipart/form-data">
    <input name="title" placeholder="Titulo de la noticia" value="<?php echo $row['title'];?>" size="110"><br><br> 
    <textarea name="content" placeholder="Contenido de la noticia" rows="10" cols="70"><?php echo $row['content'];?></textarea><br>
    <br>
    <input name="foto" type="file" id="foto" class="button" />
    <br>
    <br>
<input name="news" type="submit" value="Editar noticia"> 
</form>

Desde ya agradezco mucho tu ayuda!
Hmmm... en lugar de eliminar la foto que el usuario utilizó anteriormente, deberías darle la posibilidad (al usuario) de ver una lista de imágenes que ha subido, así será él quién deba elegir lo que hará con su contenido: darle la opción de modificar, eliminar y subir nuevas imágenes y que estas puedan ser reutilizadas.

Por el momento, para sobreescribir la imagen anterior, simplemente utiliza unlink de PHP, y sólo sube la nueva imagen si es que el archivo ha sido eliminado:

if( unlink( $ruta_archivo ) ) {
  // El archivo fue eliminado, por lo que ahora puedes mover (move_uploaded_file) el nuevo archivo.
}

Ten cuidado, porque hacer esto implica hacer un respaldo del archivo que intentas eliminar por si es que el nuevo archivo no se pudo subir o no se puede guardar en la base de datos.
skaparate escribió:Hmmm... en lugar de eliminar la foto que el usuario utilizó anteriormente, deberías darle la posibilidad (al usuario) de ver una lista de imágenes que ha subido, así será él quién deba elegir lo que hará con su contenido: darle la opción de modificar, eliminar y subir nuevas imágenes y que estas puedan ser reutilizadas.

Por el momento, para sobreescribir la imagen anterior, simplemente utiliza unlink de PHP, y sólo sube la nueva imagen si es que el archivo ha sido eliminado:

if( unlink( $ruta_archivo ) ) {
  // El archivo fue eliminado, por lo que ahora puedes mover (move_uploaded_file) el nuevo archivo.
}

Ten cuidado, porque hacer esto implica hacer un respaldo del archivo que intentas eliminar por si es que el nuevo archivo no se pudo subir o no se puede guardar en la base de datos.

El fin de semana me desgasté inútilmente tratando de hacer bien las cosas; he probado con diversos métodos y siempre me sale el mensaje warning de que no tengo permisos (servidor en ambiente Windows 7, o sea, ese warning está fuera de foco, te creo para Linux, pero en Windows, es un chiste.)

Este es el código de eliminar registros y el unlink. Funciona lo de borrar registros, en eso no hay problemas. Pero en lo de borrar las fotos elegidas en la carpeta uploads, no pasa nada:
<?php


if(isset($_GET['eliminar']))
    {
    
//id de la Noticia
    $id = $_GET['eliminar'];
    $stmt = $connection->prepare('DELETE FROM news WHERE id = :id');
    $stmt->bindParam(':id', $id); // this time, we'll use the bindParam method
    $stmt->execute();
    {
        $img = $news1['foto'];
        $id = $news1['id'];


//UNLINK foto
$realpath = "uploads/";
if(file_exists("uploads/".$img)){
       // unlink('uploads/'.$img);
        unlink($realpath($img));
        }
/*
$dir = 'uploads/';
if($dh = opendir($dir)){
    while(($foto = readdir($dh))!== false){
        if(file_exists($dir.$foto)) @unlink($dir.$foto);
    }
        closedir($dh);
}
*/






    $stmt = $connection->prepare('DELETE FROM news WHERE id = :id');
    }//while
}
?>

Estoy hasta la coronilla de qu eno me resulte y sigo sin entender qué estoy haciendo mal.
Puede que sea porque estás usando mal la función realpath:

$realpath = realpath( "uploads/" . $img );
if( $realpath ) { // realpath devuelve FALSE si la ruta no existe, así que esto es correcto.
  // ORIGINAL: unlink($realpath($img)); estás utilizando la variable en lugar de la función.
  if( unlink( $realpath ) ) {
    echo "Imagen eliminada.";
  } else {
    echo "No se pudo eliminar la imagen.";
  }
}

Te aconsejo que, si quieres y puedes, añadas alguna librería de logs. Yo he usado Klogger en PHP, que es bastante simple, pero cumple su función. Creo que las demás son demasiado complejas para la web, como Apache Log4php.
Hola, skaparate,

Oki, voy a probar tu solución.

Por cierto, ¿cómo hacer para que en pantalla aparezca el mensaje "EN esta sección no hay contenidos".

Estoy filtrando la información por categorías (o secciones) y este es el código en ver.php

<?php
if(isset($_GET['categoria'])):
$noticia = $connection->prepare("SELECT * FROM news WHERE categoria = :categoria");
$limite = 555; 
$noticia->bindParam(':categoria',$_GET['categoria']); 
$noticia->execute(); 
while($noti = $noticia->fetch(PDO::FETCH_ASSOC)):
echo '<div id="contenedor">';
echo '<div class="flotar-iz">';
echo '<img src="thumb.php?src=uploads/'.$noti['foto'].'" class="foto" style="float:left;margin-right:15px;margin-bottom:15px" /></div>';
echo '<div class="flotar-de">';
echo '<h1><a href="index.php?id='.$noti['title'].'">'.$noti['title'].'</a></h1><br><br>';
//echo '<p>'.substr($content, 0, $limite).' [...]</p><br /><br /></div>';
echo '<p>'.substr($noti['content'], 0, $limite).' [...]</p><br /><br /></div>';
echo '</div>';
endwhile;
endif;
?>
Después de $noticias->execute(), utiliza $noticia->rowCount(), que devolverá la cantidad de filas:

if( $noticia->execute() ) {
  if( $noticia->rowCount() ) {
    // Hay al menos una noticia
  } else {
    // no hay noticias
  }
}
skaparate escribió:Después de $noticias->execute(), utiliza $noticia->rowCount(), que devolverá la cantidad de filas:

if( $noticia->execute() ) {
  if( $noticia->rowCount() ) {
    // Hay al menos una noticia
  } else {
    // no hay noticias
  }
}

Fabuloso, quedó de pelos, funciona de maravillas.

Respecto de lo del unlink, está perfecto tu código, pero igual no elimina la imagen. Creo es porque en el delete olvidé que la imagen está asociada al registro y creo que primero hay que listarla ya sea con un while o un foreach. No se cuál de los dos es mejor. Huelga decir que no me ha resultado. Esto de aprender el PDO sobre la marcha es muy bueno, pero terrible, puros dolores de cabeza! jajajjaa

Saludos
En realidad no importa si está asociada a la base de datos, puesto que esta última no tiene idea de la existencia del archivo. Debe haber algo más que impide que la imagen se elimine.

Revisa el registro de errores de PHP. Si usas Xampp, el registro está en xampp\php\logs\php_error.log
Uso un stack que se llama Ampps, es simplemente fabuloso.

Dudo que sea ése el problema, porque con otros scripts el eliminado de registro y fotos opera sin problemas.
Solucionado uno de los bugs. En el index.php, vayan a la línea 104 y sustituyan:

header("Location: http://$host$uri/$extra");

por:

echo '<script type="text/javascript">window.location="index.php?id='.urlencode($_GET['id']).'";</script>';

Y con eso se elimina el problema de los headers ya enviados.

Saludos