At the moment my project is based on an application about place recommendations. I have implemented a relational database with MariaDB with data and their respective relationships. To make the requests with an API Rest I am using yii2 in my backend, it is already configured and ready to start handling my requests from the API Rest.
I’m figuring out how to implement a voting system, I already have the database to store those votes, however, I can’t figure out how to make this code work:
This is the .ts code of the implementation that I am trying to make: tab1.page.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import {
InfiniteScrollCustomEvent,
IonInfiniteScroll,
LoadingController,
} from '@ionic/angular';
import { PublicacionService } from '../services/publicacion.service';
@Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss'],
})
export class Tab1Page implements OnInit {
@ViewChild(IonInfiniteScroll) infiniteScroll: IonInfiniteScroll;
publicacion: any;
publicaciones = [];
constructor(
private publicacionService: PublicacionService,
private loadingCtrl: LoadingController
) {}
ngOnInit(): void {
this.loadPublicaciones();
}
async loadPublicaciones(event?: InfiniteScrollCustomEvent) {
const loading = await this.loadingCtrl.create({
spinner: 'crescent',
});
await loading.present();
this.publicacionService.getDatos().subscribe(
(res) => {
loading.dismiss();
this.publicaciones = res;
event?.target.complete();
},
(err) => {
console.log(err);
loading.dismiss();
}
);
}
guardarVoto() {
try {
const publicacion = this.publicacion.value;
this.publicacionService.postVote(publicacion).subscribe(
(res) => {
console.log('voto guardado');
},
(err) => {
if (err === 422) {
console.log('ya has votado por este post');
}
}
);
} catch (e) {
console.log(e);
}
}
}
This is the code for the HTML implementation: tab1.page.html
<ion-header>
<ion-toolbar color="primary">
<ion-title color="light"> NextStop </ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list *ngFor="let publicacion of publicaciones">
<ion-list-header>
<ion-label>{{publicacion.pub_titulo}}</ion-label>
</ion-list-header>
<ion-row
class="ion-padding publication-row"
tappable
[routerLink]="['/tab1', publicacion.pub_id]"
>
<ion-col size="6">
<div class="publication-tags">
<ion-label *ngFor="let etiqueta of publicacion.etiqueta"
>{{etiqueta.eti_nombre}}
</ion-label>
</div>
<div class="publication-user">
<ion-icon name="person-circle-sharp"></ion-icon>
<ion-label> {{publicacion.usuario}}</ion-label>
</div>
<div class="publication-date">
<ion-label color="medium"
>{{publicacion.pub_fecha | date: 'short'}}</ion-label
>
</div>
</ion-col>
<ion-col size="6">
<div
class="publication-image"
[style.background-image]="'url(http://nextstop.test/img/'+publicacion.imagen+')'"
></div>
</ion-col>
</ion-row>
<ion-row>
<ion-col size="12" class="publication-actions border-bottom">
<ion-button
(click)="guardarVoto()"
fill="clear"
class="ion-no-margin"
color="dark"
>
<ion-icon
name="arrow-up-outline"
color="success"
slot="icon-only"
size="small"
></ion-icon>
</ion-button>
<span class="publication-vote-count">{{publicacion.votos}}</span>
<ion-button fill="clear" class="ion-no-margin" color="dark">
<ion-icon
name="arrow-down-outline"
color="danger"
slot="icon-only"
size="small"
></ion-icon>
</ion-button>
<ion-button fill="clear" class="ion-no-margin" color="dark">
<ion-icon
name="chatbubble-outline"
color="dark"
slot="icon-only"
size="small"
></ion-icon>
<span class="publication-icon-count"
>{{publicacion.comentarios}}</span
>
</ion-button>
<ion-button fill="clear" class="ion-no-margin" color="dark">
<ion-icon
name="eye-outline"
color="dark"
slot="icon-only"
size="small"
></ion-icon>
<span class="publication-icon-count">{{publicacion.visitas}}</span>
</ion-button>
</ion-col>
</ion-row>
</ion-list>
</ion-content>
Finally the service that I am using right now: publicacion.service.ts
/* eslint-disable @typescript-eslint/naming-convention */
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
export class VotoPublicacion {
vop_fkusuario: number;
vop_fkpublicacion: number;
vop_fecha: Date;
}
@Injectable({
providedIn: 'root',
})
export class PublicacionService {
baseURL = 'http://nextstop.test/publicacion-rest';
token = '100-token';
httpHeader = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Bearer ' + this.token,
}),
};
constructor(private http: HttpClient) {}
getDatos() {
return this.http
.get(
this.baseURL +
'?expand=usuario,lugar,imagen,etiqueta,visitas,votos,comentarios'
)
.pipe(map((res: any) => res));
}
getPublicacionDetails(id: string) {
return this.http
.get(
this.baseURL +
's/' +
id +
'?expand=usuario,imagenes,etiquetas,visitas,votos,comentarios'
)
.pipe(map((res: any) => res));
}
postVote(voto_publicacion: VotoPublicacion) {
return this.http
.post(this.baseURL + 's', voto_publicacion, this.httpHeader)
.pipe(
map((res: any) => res),
catchError((error) => throwError(error.status || 'Server error.'))
);
}
}
I attach screenshots of the table that I will point to store the votes of a user when clicking the upvote button and at the same time synchronize the vote count.
This is the code in the model of Publicacion where I configure the transactions and queries to the database with Yii2:
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "publicacion".
*
* @property int $pub_id
* @property string $pub_titulo
* @property string $pub_descripcion
* @property string|null $pub_fecha
* @property int $pub_fkusuario
* @property int|null $pub_fklugar
*
* @property Comentario[] $comentarios
* @property EtiquetaPublicacion[] $etiquetaPublicacions
* @property ImagenPublicacion[] $imagenPublicacions
* @property Lugar $pubFklugar
* @property Usuario $pubFkusuario
* @property VisitaPublicacion[] $visitaPublicacions
* @property VotoPublicacion[] $votoPublicacions
*/
class Publicacion extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'publicacion';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['pub_titulo', 'pub_descripcion', 'pub_fkusuario'], 'required'],
[['pub_descripcion'], 'string'],
[['pub_fecha'], 'safe'],
[['pub_fkusuario', 'pub_fklugar'], 'integer'],
[['pub_titulo'], 'string', 'max' => 100],
[['pub_fklugar'], 'exist', 'skipOnError' => true, 'targetClass' => Lugar::className(), 'targetAttribute' => ['pub_fklugar' => 'lug_id']],
[['pub_fkusuario'], 'exist', 'skipOnError' => true, 'targetClass' => Usuario::className(), 'targetAttribute' => ['pub_fkusuario' => 'usu_id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'pub_id' => 'Pub ID',
'pub_titulo' => 'Pub Titulo',
'pub_descripcion' => 'Pub Descripcion',
'pub_fecha' => 'Pub Fecha',
'pub_fkusuario' => 'Pub Fkusuario',
'pub_fklugar' => 'Pub Fklugar',
];
}
/**
* Gets query for [[Comentarios]].
*
* @return \yii\db\ActiveQuery
*/
public function getComentarios()
{
return $this->hasMany(Comentario::className(), ['com_fkpublicacion' => 'pub_id']);
}
/**
* Gets query for [[EtiquetaPublicacions]].
*
* @return \yii\db\ActiveQuery
*/
public function getEtiquetaPublicacions()
{
return $this->hasMany(EtiquetaPublicacion::className(), ['etp_fkpublicacion' => 'pub_id']);
}
/**
* Gets query for [[ImagenPublicacions]].
*
* @return \yii\db\ActiveQuery
*/
public function getImagenPublicacions()
{
return $this->hasMany(ImagenPublicacion::className(), ['imp_fkpublicacion' => 'pub_id']);
}
/**
* Gets query for [[PubFklugar]].
*
* @return \yii\db\ActiveQuery
*/
public function getPubFklugar()
{
return $this->hasOne(Lugar::className(), ['lug_id' => 'pub_fklugar']);
}
/**
* Gets query for [[PubFkusuario]].
*
* @return \yii\db\ActiveQuery
*/
public function getPubFkusuario()
{
return $this->hasOne(Usuario::className(), ['usu_id' => 'pub_fkusuario']);
}
/**
* Gets query for [[VisitaPublicacions]].
*
* @return \yii\db\ActiveQuery
*/
public function getVisitaPublicacions()
{
return $this->hasMany(VisitaPublicacion::className(), ['vip_fkpublicacion' => 'pub_id']);
}
/**
* Gets query for [[VotoPublicacions]].
*
* @return \yii\db\ActiveQuery
*/
public function getVotoPublicacions()
{
return $this->hasMany(VotoPublicacion::className(), ['vop_fkpublicacion' => 'pub_id']);
}
public function extraFields()
{
return [
'usuario' => function($item) {
return $item->pubFkusuario->usu_nombre;
},
'lugar' => function($item) {
return $item->pubFklugar->lug_nombre;
},
'imagen' => function($item) {
return $item->imagen;
},
'imagenes' => function($item){
return $item->allImagenes;
},
'etiqueta' => function($item) {
return $item->etiqueta;
},
'etiquetas' => function($item) {
return $item->allEtiquetas;
},
'visitas' => function($item) {
return $item->allVisitas;
},
'votos' => function($item) {
return $item->allVotos;
},
'comentarios' => function($item) {
return $item->allComentarios;
}
];
}
public function getImagen()
{
$imagen = ImagenPublicacion::find()->where(['imp_fkpublicacion' => $this->pub_id])->one();
return isset($imagen) ? $imagen->imp_url : 'https://ionicframework.com/docs/img/demos/card-media.png';
}
public function getAllImagenes()
{
$imagenes_pub = ImagenPublicacion::find()->where(['imp_fkpublicacion' => $this->pub_id])->all();
$imagenes = [];
foreach ($imagenes_pub as $imagen) {
$imagenes[] = ['imp_url' => $imagen->imp_url];
}
return $imagenes;
}
public function getEtiqueta()
{
$etiquetaspub = EtiquetaPublicacion::find()->where(['etp_fkpublicacion' => $this->pub_id])->limit(2)->all();
$etiquetas = [];
$sin_etiqueta = [['eti_nombre' => '']];
foreach ($etiquetaspub as $etiqueta) {
$etiquetas[] = ['eti_nombre' => $etiqueta->etpFketiqueta->eti_nombre];
}
return count($etiquetas) > 0 ? $etiquetas : $sin_etiqueta;
}
public function getAllEtiquetas()
{
$etiquetas_pub = EtiquetaPublicacion::find()->where(['etp_fkpublicacion' => $this->pub_id])->all();
$etiquetas = [];
foreach ($etiquetas_pub as $etiqueta) {
$etiquetas[] = ['eti_nombre' => $etiqueta->etpFketiqueta->eti_nombre];
}
return $etiquetas;
}
public function getAllVisitas()
{
return Publicacion::find()->joinWith(['visitaPublicacions'])->where(['vip_fkpublicacion' => $this->pub_id])->count();
}
public function getAllVotos()
{
return Publicacion::find()->joinWith(['votoPublicacions'])->where(['vop_fkpublicacion' => $this->pub_id])->count();
}
public function getAllComentarios()
{
return Publicacion::find()->joinWith(['comentarios'])->where(['com_fkpublicacion' => $this->pub_id])->count();
}
}