Commit 78116f17 authored by Yevgeniy Agrafenin's avatar Yevgeniy Agrafenin

#4 Добавил модель Review.cs создал миграцию. Добавил представление создания…

#4 Добавил модель Review.cs создал миграцию. Добавил представление создания отзыва, добавил контроллер юзера.
parent dc4904b2
...@@ -7,5 +7,6 @@ namespace CafeCritic.Context; ...@@ -7,5 +7,6 @@ namespace CafeCritic.Context;
public class CafeDbContext: IdentityDbContext<User> public class CafeDbContext: IdentityDbContext<User>
{ {
public required DbSet<Cafe> Cafes { get; set; } public required DbSet<Cafe> Cafes { get; set; }
public DbSet<Review> Reviews { get; set; }
public CafeDbContext(DbContextOptions<CafeDbContext> options) : base(options){} public CafeDbContext(DbContextOptions<CafeDbContext> options) : base(options){}
} }
\ No newline at end of file
using System.ComponentModel.DataAnnotations;
using CafeCritic.Context;
using CafeCritic.Enums;
using CafeCritic.Mappers;
using CafeCritic.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace CafeCritic.Controllers;
public class UserController : Controller
{
private readonly UserManager<User> _userManager;
private readonly CafeDbContext _db;
public UserController(UserManager<User> userManager, CafeDbContext db)
{
_userManager = userManager;
_db = db;
}
// GET
[HttpGet]
[Authorize]
[Display(Name = "AddReview")]
public async Task<IActionResult> AddReviewAsync(string? reviw, int cafeId, Rating rating)
{
if (reviw == null)
{
return NotFound();
}
if (_db.Reviews.Any(r => r.CafeId == cafeId && r.UserId == _userManager.GetUserId(User)))
{
return BadRequest("Комментарий уже добавлен.");
}
var reviewDb = new Review
{
Description = reviw,
DateOfCreat = DateTime.UtcNow,
UserId = _userManager.GetUserId(User),
CafeId = cafeId,
Rating = (int)rating
};
await _db.Reviews.AddAsync(reviewDb);
await _db.SaveChangesAsync();
var answer = await _db.Reviews.Include(a => a.User)
.Include(a => a.Cafe)
.OrderBy(a => a.Id)
.LastOrDefaultAsync();
var viewModel = UserMapper.ReviewReviewViewModel(answer);
return PartialView(viewModel);
}
}
\ No newline at end of file
using CafeCritic.Models;
using CafeCritic.ViewModels;
namespace CafeCritic.Mappers;
public class UserMapper
{
public static ReviewViewModel ReviewReviewViewModel(Review review)
{
return new ReviewViewModel
{
Id = review.Id,
DateOfCreat = review.DateOfCreat,
Description = review.Description,
Rating = review.Rating,
Cafe = review.Cafe,
CafeId = review.CafeId,
User = review.User,
UserId = review.UserId
};
}
}
\ No newline at end of file
// <auto-generated />
using System;
using System.Collections.Generic;
using CafeCritic.Context;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace CafeCritic.Migrations
{
[DbContext(typeof(CafeDbContext))]
[Migration("20231120173706_AddReviewModel")]
partial class AddReviewModel
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.14")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("CafeCritic.Models.Cafe", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Description")
.HasColumnType("text");
b.Property<List<string>>("Gallery")
.HasColumnType("text[]");
b.Property<string>("Image")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Cafes");
});
modelBuilder.Entity("CafeCritic.Models.Review", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CafeId")
.HasColumnType("integer");
b.Property<DateTime>("DateOfCreat")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("Rating")
.HasColumnType("integer");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CafeId");
b.HasIndex("UserId");
b.ToTable("Reviews");
});
modelBuilder.Entity("CafeCritic.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("CafeCritic.Models.Review", b =>
{
b.HasOne("CafeCritic.Models.Cafe", "Cafe")
.WithMany("Reviews")
.HasForeignKey("CafeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CafeCritic.Models.User", "User")
.WithMany("Reviews")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Cafe");
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("CafeCritic.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("CafeCritic.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CafeCritic.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("CafeCritic.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("CafeCritic.Models.Cafe", b =>
{
b.Navigation("Reviews");
});
modelBuilder.Entity("CafeCritic.Models.User", b =>
{
b.Navigation("Reviews");
});
#pragma warning restore 612, 618
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace CafeCritic.Migrations
{
/// <inheritdoc />
public partial class AddReviewModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<List<string>>(
name: "Gallery",
table: "Cafes",
type: "text[]",
nullable: true);
migrationBuilder.CreateTable(
name: "Reviews",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Description = table.Column<string>(type: "text", nullable: true),
Rating = table.Column<int>(type: "integer", nullable: false),
UserId = table.Column<string>(type: "text", nullable: false),
CafeId = table.Column<int>(type: "integer", nullable: false),
DateOfCreat = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Reviews", x => x.Id);
table.ForeignKey(
name: "FK_Reviews_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Reviews_Cafes_CafeId",
column: x => x.CafeId,
principalTable: "Cafes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Reviews_CafeId",
table: "Reviews",
column: "CafeId");
migrationBuilder.CreateIndex(
name: "IX_Reviews_UserId",
table: "Reviews",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Reviews");
migrationBuilder.DropColumn(
name: "Gallery",
table: "Cafes");
}
}
}
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Collections.Generic;
using CafeCritic.Context; using CafeCritic.Context;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
...@@ -33,6 +34,9 @@ namespace CafeCritic.Migrations ...@@ -33,6 +34,9 @@ namespace CafeCritic.Migrations
b.Property<string>("Description") b.Property<string>("Description")
.HasColumnType("text"); .HasColumnType("text");
b.Property<List<string>>("Gallery")
.HasColumnType("text[]");
b.Property<string>("Image") b.Property<string>("Image")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
...@@ -46,6 +50,39 @@ namespace CafeCritic.Migrations ...@@ -46,6 +50,39 @@ namespace CafeCritic.Migrations
b.ToTable("Cafes"); b.ToTable("Cafes");
}); });
modelBuilder.Entity("CafeCritic.Models.Review", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CafeId")
.HasColumnType("integer");
b.Property<DateTime>("DateOfCreat")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("Rating")
.HasColumnType("integer");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CafeId");
b.HasIndex("UserId");
b.ToTable("Reviews");
});
modelBuilder.Entity("CafeCritic.Models.User", b => modelBuilder.Entity("CafeCritic.Models.User", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id")
...@@ -242,6 +279,25 @@ namespace CafeCritic.Migrations ...@@ -242,6 +279,25 @@ namespace CafeCritic.Migrations
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", (string)null);
}); });
modelBuilder.Entity("CafeCritic.Models.Review", b =>
{
b.HasOne("CafeCritic.Models.Cafe", "Cafe")
.WithMany("Reviews")
.HasForeignKey("CafeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CafeCritic.Models.User", "User")
.WithMany("Reviews")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Cafe");
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
...@@ -292,6 +348,16 @@ namespace CafeCritic.Migrations ...@@ -292,6 +348,16 @@ namespace CafeCritic.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("CafeCritic.Models.Cafe", b =>
{
b.Navigation("Reviews");
});
modelBuilder.Entity("CafeCritic.Models.User", b =>
{
b.Navigation("Reviews");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }
......
...@@ -6,4 +6,6 @@ public class Cafe ...@@ -6,4 +6,6 @@ public class Cafe
public required string Title { get; set; } public required string Title { get; set; }
public string? Description { get; set; } public string? Description { get; set; }
public required string Image { get; set; } public required string Image { get; set; }
public List<Review> Reviews { get; set; } = new();
public List<string>? Gallery { get; set; } = new();
} }
\ No newline at end of file
namespace CafeCritic.Models;
public class Review
{
public int Id { get; set; }
public string? Description { get; set; }
public int Rating { get; set; }
public User? User { get; set; }
public string UserId { get; set; }
public Cafe? Cafe { get; set; }
public int CafeId { get; set; }
public DateTime DateOfCreat { get; set; }
}
\ No newline at end of file
...@@ -4,5 +4,5 @@ namespace CafeCritic.Models; ...@@ -4,5 +4,5 @@ namespace CafeCritic.Models;
public class User: IdentityUser public class User: IdentityUser
{ {
public List<Review> Reviews { get; set; } = new();
} }
\ No newline at end of file
using CafeCritic.Models;
namespace CafeCritic.ViewModels;
public class CafeDetailViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Image { get; set; }
public string? Description { get; set; }
public List<Review>? Reviews { get; set; } = new();
public List<string>? Gallery { get; set; } = new();
}
\ No newline at end of file
using CafeCritic.Models;
namespace CafeCritic.ViewModels;
public class ReviewViewModel
{
public int Id { get; set; }
public string? Description { get; set; }
public int Rating { get; set; }
public User? User { get; set; }
public string UserId { get; set; }
public Cafe? Cafe { get; set; }
public int CafeId { get; set; }
public DateTime DateOfCreat { get; set; }
}
\ No newline at end of file
@model Cafe @using CafeCritic.Enums
@model CafeCritic.ViewModels.CafeDetailViewModel
@{ @{
ViewBag.Title = "Подробнее"; ViewBag.Title = "Подробнее";
...@@ -7,8 +8,115 @@ ...@@ -7,8 +8,115 @@
<h2>Подробнее о заведении</h2> <h2>Подробнее о заведении</h2>
<img src="~/@Model.Image" alt="PhotoCafe" style="width: 18rem;"> <div class="container">
<h2>Название: - @Model.Title</h2> <div class="row justify-content-around">
<p>Описание: - @Model.Description</p> <div class="col-3">
<img src="~/@Model.Image" alt="PhotoCafe" style="width: 18rem;">
</div>
<div class="col-7 pt-5">
<div class="row">
<div class="col-4">
<h2>Название: - @Model.Title</h2>
</div>
</div>
<div class="description">
<p>Описание: - @Model.Description</p>
</div>
</div>
</div>
<div class="row d-flex mt-5">
@if (Model.Gallery != null)
{
@foreach (var image in Model.Gallery)
{
<div class="col-3 pe-0 ps-0 m-0">
<img src="~/@image" width="200px" height="160px" alt="galleryImage">
</div>
}
}
else
{
<h4>В галерее пока нет фото</h4>
}
</div>
<div>
<h4>Рейтинг:</h4>
</div>
<div>
<h4>Отзывы:</h4>
<table class="table table-bordered">
<tr>
<th scope="col">Логин</th>
<th scope="col">Дата создания</th>
<th scope="col">Оценка</th>
<th scope="col">Отзыв</th>
</tr>
@foreach (var review in Model.Reviews.OrderByDescending(r => r.DateOfCreat))
{
<tr>
<td>@review.User.UserName</td>
<td>@review.DateOfCreat</td>
<td>@review.Rating</td>
<td>@review.Description</td>
</tr>
}
</table>
<div class="d-flex justify-content-center">
<div class="card card-width">
<div class="card-body d-flex justify-content-center">
@foreach (var review in Model.Reviews)
{
if (review.UserId != ViewBag.UserId)
{
<form id="addUser" enctype="multipart/form-data">
<div class="text-center">
<h6>Оставить отзыв</h6>
</div>
<div>
<input type="text" class="form-control my-2 text-dark" name="answer" id="writeAnswer" placeholder="Отзыв">
</div>
<select class="form-control" id="rating">
@foreach (var rating in ViewBag.Rating)
{
<option value="@rating">@rating</option>
}
</select>
<input type="button" id="submit" class="btn btn-info w-100 mt-3 text-white" value="Введите ответ"/>
</form>
}
else
{
<p>Вы уже оставляли комментарий</p>
}
}
</div>
</div>
</div>
</div>
</div>
<a asp-controller="Cafe" asp-action="Index">Список заведений.</a> <a asp-controller="Cafe" asp-action="Index">Список заведений.</a>
@section Scripts
{
<script>
let scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight);
$(document).ready(function (){
$('#submit').click(function (event){
event.preventDefault();
let review = $('#writeAnswer').val();
let rating = $('#rating').val();
review = encodeURIComponent(review);
rating = encodeURIComponent(rating);
$('#results').append($('<tr>').load('@Url.Action("AddReview", "User")?review=' + review +
'&cafeId=@Model.Id'+'&rating='+rating));
window.scrollTo(0, scrollHeight);
});
});
</script>
}
@model CafeCritic.ViewModels.ReviewViewModel
<td>@Model.User.UserName</td>
<td>@Model.DateOfCreat</td>
<td>@Model.Rating</td>
<td>@Model.Description</td>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment