Terzo passo: strutturiamo le news

Per la gestione delle news procederemo a step gestendo all’inizio una struttura semplice e andandola a complicare arricchendola di funzionalità mano a mano.

Creiamo la tabella iniziale sul database con il seguente codice

CREATE TABLE `news` (
 `id` int(11) NOT NULL,
 `title` varchar(255) NOT NULL,
 `abstract` varchar(255) NOT NULL,
 `content` text NOT NULL,
 `start_date` date NOT NULL,
 `end_date` date NOT NULL,
 `published` tinyint(1) NOT NULL DEFAULT '0',
 `deleted` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Gii, il tool per la generazione del codice a partire dal DB incluso in Yii, è già abilitato se siamo in modalità dev. Apriamolo digitando index.php?r=gii come indirizzo ed andiamo a generare il Modello per le nostre news.

Impostiamo il Table Name a news ed la Model Class a News, lasciando il resto come è di default. Preview, Generate, ed il modello è generato senza dover scrivere una riga di codice!
Andiamo a generare le operazioni CRUD impostando Model Class app\models\News, Search Model Class app\models\NewsSearch, Controller Class app\controllers\NewsController e View Path @app/views/news. Di nuovo Preview e Generate.
Finito! Adesso possiamo quindi visualizzare, inserire, modificare e cancellare le varie news.

Aggiungiamo l’immagine di copertina.
Alla nostra tabella news aggiungiamo il campo cover di tipo varchar di 200 caratteri (ci memorizzeremo l’url del file caricato).

Nel modello News.php andiamo ad aggiungere dentro alla classe public $file per mantenere traccia del file caricato ed inseriamo la regola [[‘file’], ‘file’] per segnalare che è di tipo file.
Aggiungiamo anche la regola [[‘cover’], ‘string’, ‘max’ => 200] per segnalare che il campo cover è una stringa di max 200 caratteri e chiudiamo il file del modello.

Nel modello NewsSearch.php andiamo a modificare la funzione search valorizzando in $query->andFilterWhere il campo deleted sempre a 0 per non vedere gli articoli che sono stati cancellati.

Modifichiamo la view _form.php che è stata generata all’interno della cartella news.
Trasformiamo la definizione del form in <?php $form = ActiveForm::begin([‘options’ => [‘enctype’ => ‘multipart/form-data’]]); ?> per far si che l’upload dei file sia supportato ed aggiungiamo il campo file tramite l’istruzione <?= $form->field($model, ‘file’)->fileInput(); ?>.
Già che ci siamo, cancelliamo il rendering del campo deleted e trasformiamo quello del campo published in una dropdown con <?= $form->field($model, ‘published’)->dropDownList([0 => ‘Inactive’, 1=> ‘Active’]); ?>

Creiamo la cartella covers all’interno della cartella web. Caricheremo le nostre immagini in questa cartella.

Infine, andiamo a modificare il controller NewsController.php con il codice riportato qui sotto. Ho inserito vari commenti dove necessario nel codice.

<?php

namespace app\controllers;

use Yii;
use app\models\News;
use app\models\NewsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;
/**
 * NewsController implements the CRUD actions for News model.
 */
class NewsController extends Controller {

 public function behaviors() {
  return [
   'verbs' => [
    'class' => VerbFilter::className(),
    'actions' => [
     'delete' => ['post'],
    ],
   ],
  ];
 }

 /**
  * Lists all News models.
  * @return mixed
  */
 public function actionIndex() {
  $searchModel = new NewsSearch();
  $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

  return $this->render('index', [
   'searchModel' => $searchModel,
   'dataProvider' => $dataProvider,
  ]);
 }

 /**
  * Displays a single News model.
  * @param integer $id
  * @return mixed
  */
 public function actionView($id) {
  return $this->render('view', ['model' => $this->findModel($id),]);
 }

 /**
  * Creates a new News model.
  * If creation is successful, the browser will be redirected to the 'view' page.
  * @return mixed
  */
 public function actionCreate() {
  $model = new News();
  if ($model->load(Yii::$app->request->post())) {
   if($model->save()){
    Yii::$app->session->setFlash('success', 'News creata con successo');

    //Ricavo il file caricato e se presente lo salvo
    $image = UploadedFile::getInstance($model, 'file');
    if($image){
     $imageName  = "cover_".$model->id.".".$model->file->extension;
     $model->file = $image;
     $model->file->saveAs('covers/'.$imageName);

     //Salvo il path del file caricato
     $model->cover = $imageName;
     if($model->save()){
      Yii::$app->session->setFlash('success', 'Cover caricata con successo');
     }else{
      Yii::$app->session->setFlash('error', 'Errore durante il caricamento della cover');
     }
    }

    //Redirect
    return $this->redirect(['view', 'id' => $model->id]);
   }else{
    Yii::$app->session->setFlash('error', 'Errore durante la creazione della news');
    return $this->redirect(['create']);
   }
  } else {
   return $this->render('create', ['model' => $model]);
  }
 }

 /**
  * Updates an existing News model.
  * If update is successful, the browser will be redirected to the 'view' page.
  * @param integer $id
  * @return mixed
  */
 public function actionUpdate($id) {
  $model = $this->findModel($id);
  if ($model->load(Yii::$app->request->post())) {
   if($model->save()){
    //Ricavo il file caricato e se presente lo salvo
    $image = UploadedFile::getInstance($model, 'file');
    if($image){
     $imageName  = "cover_".$model->id.".".$image->extension;
     $model->file = $image;
     $model->file->saveAs('covers/'.$imageName);

     //Salvo il path del file caricato
     $model->cover = $imageName;
     if($model->save()){
      Yii::$app->session->setFlash('success', 'Cover caricata con successo');
     }else{
      Yii::$app->session->setFlash('error', 'Errore durante il caricamento della cover');
     }
    }

    return $this->redirect(['view', 'id' => $model->id]);
   }
  } else {
    return $this->render('update', ['model' => $model]);
  }
 }

 /**
  * Deletes an existing News model.
  * If deletion is successful, the browser will be redirected to the 'index' page.
  * @param integer $id
  * @return mixed
  */
 public function actionDelete($id) {
  $model = $this->findModel($id);
  $model->deleted = 1;

  if($model->save()){ Yii::$app->session->setFlash('success', 'News cancellata');
  }else{ Yii::$app->session->setFlash('error', 'Errore durante la cancellazione della news'); }
  return $this->redirect(['index']);
 }

 /**
  * Finds the News model based on its primary key value.
  * If the model is not found, a 404 HTTP exception will be thrown.
  * @param integer $id
  * @return News the loaded model
  * @throws NotFoundHttpException if the model cannot be found
  */
 protected function findModel($id) {
  if (($model = News::find()->andWhere(['id' => $id])->andWhere(['deleted' => 0])->one()) !== null) {
   return $model;
  } else {
   throw new NotFoundHttpException('The requested page does not exist.');
  }
 }
}

Abbiamo quindi un sistema base per le news generato in modo veloce, con pochissime modifiche al codice ottenuto da Gii.