Commit ea32df3f authored by TTrueBenji's avatar TTrueBenji

Добавить валидацию.

***
- Добавил валидацию полей формы создания и редактирования телефона.
- Пофиксил ошибку сравнения строк при проверке названия телефона.
- Изменил форму добавления телефона.
- Вынес данные о производителе телефонов в отдельную таблицу.
- Вынес форму добавления телефона в частичное представление.
- Добавил клиентскую валидацию полей в формах создания и редактирования данных телефона.
***
parent d2c688e2
using Microsoft.AspNetCore.Mvc;
using PhoneStore.Models;
using PhoneStore.ViewModels;
namespace PhoneStore.Controllers
{
public class BrandsController : Controller
{
private readonly MobileContext _db;
public BrandsController(MobileContext db)
{
_db = db;
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(CreateBrandViewModel model)
{
_db.Brands.Add(new Brand
{
Name = model.Name
});
_db.SaveChanges();
return RedirectToAction("Index", "Phones");
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using PhoneStore.Models;
namespace PhoneStore.Controllers
{
public class PhoneValidatorController : Controller
{
private readonly MobileContext _db;
public PhoneValidatorController(MobileContext db)
{
_db = db;
}
[AcceptVerbs("GET", "POST")]
public bool CheckName(string name)
{
return !_db.Phones
.AsEnumerable()
.Any(p => p.Name.Equals(name, StringComparison.CurrentCulture));
}
}
}
\ No newline at end of file
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using PhoneStore.Models;
using PhoneStore.Services;
......@@ -29,35 +32,54 @@ namespace PhoneStore.Controllers
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Index(int? id, string? name)
public IActionResult Index(int? brandId, string name)
{
var phones = _db.Phones.AsQueryable();
if (id.HasValue)
{
phones = _db.Phones.Where(p => p.Id == id);
}
if (!string.IsNullOrEmpty(name))
{
phones = phones.Where(p => string.Equals(p.Name, name, StringComparison.CurrentCultureIgnoreCase));
}
return View(phones.ToList());
IQueryable<Phone> phones = _db.Phones
.Include(p => p.Brand)
.AsQueryable();
IEnumerable<Brand> brands = _db.Brands;
if (brandId is > 0)
phones = _db.Phones.Where(p => p.BrandId == brandId);
return View(new IndexViewModel
{
Brands = brands,
Phones = phones.ToList()
});
}
[HttpGet]
public IActionResult Create()
{
return View();
PhoneCreateViewModel model = new PhoneCreateViewModel
{
Brands = _db.Brands.ToList()
};
return View(model);
}
[HttpPost]
public IActionResult Create(Phone phone)
public IActionResult Create(PhoneCreateViewModel model)
{
if (ModelState.IsValid)
{
_db.Phones.Add(phone);
_db.Phones.Add(new Phone
{
Name = model.Name,
Price = (decimal)model.Price!,
BrandId = model.BrandId
});
_db.SaveChanges();
return RedirectToAction("Index");
}
model.Brands = _db.Brands.ToList();
return View("Create", model);
}
[HttpGet]
public IActionResult Delete(int phoneId)
{
......@@ -89,7 +111,15 @@ namespace PhoneStore.Controllers
{
return BadRequest();
}
return View(phone);
PhoneCreateViewModel model = new PhoneCreateViewModel
{
Id = phone.Id,
Name = phone.Name,
Price = phone.Price,
BrandId = (int)phone.BrandId,
Brands = _db.Brands.ToList()
};
return View(model);
}
[HttpPost]
......
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PhoneStore.Models;
namespace PhoneStore.Migrations
{
[DbContext(typeof(MobileContext))]
[Migration("20220630102229_BrandEntity")]
partial class BrandEntity
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "5.0.17");
modelBuilder.Entity("PhoneStore.Models.Basket", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("PhoneId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PhoneId");
b.ToTable("Baskets");
});
modelBuilder.Entity("PhoneStore.Models.Brand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Brands");
});
modelBuilder.Entity("PhoneStore.Models.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Address")
.HasColumnType("TEXT");
b.Property<string>("ContactPhone")
.HasColumnType("TEXT");
b.Property<int>("PhoneId")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PhoneId");
b.ToTable("Orders");
});
modelBuilder.Entity("PhoneStore.Models.Phone", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("BrandId")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<decimal>("Price")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("BrandId");
b.ToTable("Phones");
});
modelBuilder.Entity("PhoneStore.Models.Basket", b =>
{
b.HasOne("PhoneStore.Models.Phone", "Phone")
.WithMany()
.HasForeignKey("PhoneId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Phone");
});
modelBuilder.Entity("PhoneStore.Models.Order", b =>
{
b.HasOne("PhoneStore.Models.Phone", "Phone")
.WithMany()
.HasForeignKey("PhoneId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Phone");
});
modelBuilder.Entity("PhoneStore.Models.Phone", b =>
{
b.HasOne("PhoneStore.Models.Brand", "Brand")
.WithMany()
.HasForeignKey("BrandId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Brand");
});
#pragma warning restore 612, 618
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
namespace PhoneStore.Migrations
{
public partial class BrandEntity : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Company",
table: "Phones");
migrationBuilder.AddColumn<int>(
name: "BrandId",
table: "Phones",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "Brands",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Brands", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Phones_BrandId",
table: "Phones",
column: "BrandId");
migrationBuilder.AddForeignKey(
name: "FK_Phones_Brands_BrandId",
table: "Phones",
column: "BrandId",
principalTable: "Brands",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Phones_Brands_BrandId",
table: "Phones");
migrationBuilder.DropTable(
name: "Brands");
migrationBuilder.DropIndex(
name: "IX_Phones_BrandId",
table: "Phones");
migrationBuilder.DropColumn(
name: "BrandId",
table: "Phones");
migrationBuilder.AddColumn<string>(
name: "Company",
table: "Phones",
type: "TEXT",
nullable: true);
}
}
}
......@@ -31,6 +31,20 @@ namespace PhoneStore.Migrations
b.ToTable("Baskets");
});
modelBuilder.Entity("PhoneStore.Models.Brand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Brands");
});
modelBuilder.Entity("PhoneStore.Models.Order", b =>
{
b.Property<int>("Id")
......@@ -62,8 +76,8 @@ namespace PhoneStore.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Company")
.HasColumnType("TEXT");
b.Property<int>("BrandId")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.HasColumnType("TEXT");
......@@ -73,6 +87,8 @@ namespace PhoneStore.Migrations
b.HasKey("Id");
b.HasIndex("BrandId");
b.ToTable("Phones");
});
......@@ -97,6 +113,17 @@ namespace PhoneStore.Migrations
b.Navigation("Phone");
});
modelBuilder.Entity("PhoneStore.Models.Phone", b =>
{
b.HasOne("PhoneStore.Models.Brand", "Brand")
.WithMany()
.HasForeignKey("BrandId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Brand");
});
#pragma warning restore 612, 618
}
}
......
namespace PhoneStore.Models
{
public class Brand
{
public int Id { get; set; }
public string Name { get; set; }
}
}
\ No newline at end of file
......@@ -7,6 +7,7 @@ namespace PhoneStore.Models
public DbSet<Phone> Phones { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Basket> Baskets { get; set; }
public DbSet<Brand> Brands { get; set; }
public MobileContext(DbContextOptions<MobileContext> options) : base(options)
{
......
......@@ -4,7 +4,8 @@ namespace PhoneStore.Models
{
public int Id { get; set; }
public string Name { get; set; }
public string Company { get; set; }
public decimal Price { get; set; }
public int? BrandId { get; set; }
public Brand Brand { get; set; }
}
}
\ No newline at end of file
......@@ -18,6 +18,7 @@
<ItemGroup>
<Folder Include="Migrations" />
<Folder Include="Pages" />
</ItemGroup>
</Project>
namespace PhoneStore.ViewModels
{
public class CreateBrandViewModel
{
public string Name { get; set; }
}
}
\ No newline at end of file
using System.Collections;
using System.Collections.Generic;
using PhoneStore.Models;
namespace PhoneStore.ViewModels
{
public class IndexViewModel
{
public IEnumerable<Phone> Phones { get; set; }
public IEnumerable<Brand> Brands { get; set; }
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using PhoneStore.Models;
namespace PhoneStore.ViewModels
{
public class PhoneCreateViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "Поле обязательно для заполнения")]
[Remote("CheckName", "PhoneValidator", ErrorMessage = "Имя занято")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Минимальная длина 3 символа, максимальная - 50")]
public string Name { get; set; }
[Required(ErrorMessage = "Поле обязательно для заполнения")]
[Range(1000, 50000, ErrorMessage = "Значение не валидно, вводить можно знаение от 1000 до 5000")]
public decimal? Price { get; set; }
public int BrandId { get; set; }
public List<Brand> Brands { get; set; }
}
}
\ No newline at end of file
......@@ -19,7 +19,7 @@
<tr>
<td>@basket.PhoneId</td>
<td>@basket.Phone.Name</td>
<td>@basket.Phone.Company</td>
<td>@basket.Phone.Brand.Name</td>
<td>@basket.Phone.Price</td>
<td><a asp-route-id="@basket.PhoneId" asp-action="Remove" asp-controller="Baskets" class="btn btn-outline-danger">Удалить из корзины</a></td>
</tr>
......
@model CreateBrandViewModel
@{
ViewBag.Title = "Добавление бренда";
Layout = "_Layout";
}
<h2>Заполните форму</h2>
<div class="row">
<div class="phone_add_form col-md-6">
<form asp-action="Create" asp-controller="Brands" method="post">
<div class="form_row">
<label for="name">
Наименование
<input id="name" type="text" asp-for="Name">
</label>
<button class="btn btn-outline-warning">Отправить</button>
</div>
</form>
</div>
</div>
\ No newline at end of file
@model Phone
@model PhoneCreateViewModel
@{
ViewBag.Title = "title";
......@@ -9,21 +9,18 @@
<div class="row">
<div class="phone_add_form col-md-6">
<form asp-action="Create" asp-controller="Phones" method="post">
<div class="form_row">
<label for="name">
Наименование
<input id="name" type="text" asp-for="Name">
</label>
<label for="company">
Производитель
<input id="company" type="text" asp-for="Company">
</label>
<label for="price">
Стоимость
<input id="price" type="text" asp-for="Price">
</label>
@{
await Html.RenderPartialAsync("PartialViews/PhoneFormPartialView", Model);
}
<button class="btn btn-outline-warning">Отправить</button>
</div>
</form>
</div>
</div>
@section Scripts
{
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
@model Phone
@model PhoneCreateViewModel
@{
ViewBag.Title = "Редактирование";
......@@ -10,9 +10,15 @@
@{
await Html.RenderPartialAsync("PartialViews/PhoneFormPartialView");
}
<input type="text" hidden asp-for="Id">
<input type="text" hidden asp-for="@Model.Id">
<button type="submit"
class="btn btn-outline-warning">Изменить</button>
</form>
</div>
</div>
@section Scripts
{
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
\ No newline at end of file
@model List<Phone>
@model IndexViewModel
@{
ViewBag.Title = "Смартфоны";
Layout = "_Layout";
}
@if (Model.Count == 0)
@if (Model.Phones.Count() == 0)
{
<h2>Список пуст.</h2>
}
else
{
<form asp-action="Index" asp-controller="Phones" method="get">
<select name="brandId" class="form-control">
<option>Выберите бренд</option>
@foreach (var brand in Model.Brands)
{
<option value="@brand.Id">@brand.Name</option>
}
</select>
<button type="submit" class="btn btn-outline-warning">Отправить</button>
</form>
<h2>Список устройств</h2>
<table style="width: 100%">
<tr>
......@@ -20,12 +30,12 @@ else
<th></th>
<th></th>
</tr>
@foreach (var phone in Model)
@foreach (var phone in Model.Phones)
{
<tr>
<td><img class="phone_img" src="" alt="картинка паламатая"/></td>
<td>@phone.Name</td>
<td>@phone.Company</td>
<td>@phone.Brand.Name</td>
<td>@phone.Price</td>
<td>
<div class="dropdown">
......@@ -70,3 +80,4 @@ else
<div class="d-flex justify-content-end my-5">
<a class="btn btn-outline-info" asp-action="Create" asp-controller="Phones">Добавить устройство</a>
</div>
@model Phone
<label for="name">
Наименование
<input id="name" type="text" asp-for="Name">
</label>
<label for="company">
Производитель
<input id="company" type="text" asp-for="Company">
</label>
<label for="price">
Стоимость
<input id="price" type="text" asp-for="Price">
</label>
\ No newline at end of file
@model PhoneCreateViewModel
<div class="mb-3">
<label asp-for="Name" class="form-label">Наименование</label>
<input type="text" class="form-control" asp-for="Name">
<div class="form-text">
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="mb-3">
<label asp-for="Price" class="form-label">Стоимость</label>
<input type="text" class="form-control" asp-for="Price">
<div class="form-text">
<span asp-validation-for="Price" class="text-danger"></span>
</div>
</div>
<div class="mb-3">
<label asp-for="BrandId" class="form-label">Производитель</label>
<select asp-for="BrandId" class="form-select">
@foreach (var brand in Model.Brands)
{
<option value="@brand.Id">@brand.Name</option>
}
</select>
</div>
......@@ -24,6 +24,9 @@
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Orders" asp-action="Index">Заказы</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Brands" asp-action="Create">Добавить бренд</a>
</li>
</ul>
</div>
</div>
......
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