Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
Страницы 1
Добрый вечер!
Я новичок в Laravel. Моего знания хватило для создания простого lending page, с картинками портфолио, и админки для размещения их на сайте.
Возникла задача, а как к ней подступиться не знаю. Создать корзину. Принцип действия корзины прост, кликнуть на картинку (витрина на сайте), картинка помещается в корзину (т.е. формируется заказ), и отправляется файл (например excel) владельцу сайта.
Я понимаю, что это работа с сессиями. То есть информация которая должна находится в корзине - записывается в сессию и хранится там до того момента как нужно корзину очистить. При этом что хранить в сессии - это уже зависит от функционала, как правило это идентификатор необходимого товара. Вот только как и с чего начать в Laravel не знаю. Может у кого то есть пример похожего кода?
Не в сети
ну вот тебе с одного из моих проектов
app/Services/Cart.php:
<?php namespace App\Services;
use App\CartItem;
use App\Product;
use App\Exceptions\CartException as Exception;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Request;
use Illuminate\Session\Store as Session;
class Cart {
/**
* @var Session
*/
private $session;
/**
* @var Guard
*/
private $auth;
/**
* @var CartItem
*/
private $model;
/**
* @var string
*/
private $code;
function __construct(Request $request, Guard $auth, CartItem $model)
{
$this->session = $request->session();
$this->auth = $auth;
$this->model = $model;
$this->code = $this->session->get('cart.code');
if (is_null($this->code)) {
$this->createNewCart();
}
}
public function code()
{
return $this->code;
}
public function clear()
{
$this->session->remove('cart');
$this->model->whereCode($this->code)->delete();
$this->createNewCart();
}
/**
* Сумма стоимостей всех позиций в корзине. Итоговая сумма на чеке.
*
* @return float
*/
public function total()
{
$total = $this->session->get('cart.total');
if (is_null($total)) {
/** @var Collection $items */
$items = $this->model->with([ 'product' => function ($query) { $query->select([ 'id', 'price' ]); } ])
->whereCode($this->code)
->get([ 'product_id', 'quantity' ]);
$total = $items->sum(function ($item) { return $item->quantity * $item->product->price; });
$this->session->set('cart.total', $total);
}
return $total;
}
/**
* Суммарное количество единиц товара в заказе.
*
* @return int
*/
public function count()
{
$count = $this->session->get('cart.count');
if (is_null($count)) {
$count = $this->model->whereCode($this->code)->sum('quantity');
$this->session->set('cart.count', $count);
}
return $count;
}
/**
* Позиции заказа.
*
* @param array $columns
* @param bool $lock
*
* @return Collection
*/
public function items($columns = [ '*' ], $lock = false)
{
if ($lock) {
return $this->model->with(
[
'product' => function ($query) {
$query->lockForUpdate();
}
]
)->whereCode($this->code)->latest('created_at')->lockForUpdate()->get($columns);
}
return $this->model->with('product')->whereCode($this->code)->latest('created_at')->get($columns);
}
/**
* Изменения количества позиции в заказе. Проверяет наличие на складе. Не проверяет баланс пользователя.
*
* @param string|array $condition
* @param int $quantity
*
* @throws Exception
*/
public function setQuantity($condition, $quantity)
{
if (!is_array($condition)) {
$condition = [ 'product_id' => $condition ];
}
// проверка наличия на складе
$item = $this->model->with('product')->whereCode($this->code)->where($condition)->first();
if ($quantity > $item->product->quantity) {
throw new Exception(setting('message.not_enough_in_stock') ?: 'Количество на складе ограничено.');
}
$item->update(compact('quantity'));
$this->clearCounters();
}
/**
* @param Product $product
* @param int $quantity
*
* @return CartItem
* @throws Exception
*/
public function addItem($product, $quantity = 1)
{
if ($this->auth->guest()) {
abort(401);
}
if (!$this->auth->user()->is_admin) {
// перед добавлением товара в корзину надо проверить баланс пользователя
// если баллов не хватает, контроллеру возвращается false для того
// чтобы тот мог уведомить пользователя надлежащим способом
$possible_total = $this->total() + $product->price;
if ($possible_total > $this->auth->user()->balance) {
throw new Exception(
setting(
'message.not_enough_points'
) ?: 'На вашем балансе недостаточно баллов для выполнения этого действия.'
);
}
}
// если товар уже есть в корзине - только увеличим его количество
/** @var CartItem $item */
$item = $this->model->whereCode($this->code)->whereProductId($product->getKey())->first();
if ($item) {
// проверка наличия на складе
if ($item->quantity + $quantity > $product->quantity) {
throw new Exception(setting('message.not_enough_in_stock') ?: 'Количество на складе ограничено.');
}
$item->increment('quantity', $quantity);
$this->clearCounters();
return $item;
}
// проверка наличия на складе
if ($quantity > $product->quantity) {
throw new Exception(setting('message.not_enough_in_stock') ?: 'Количество на складе ограничено.');
}
// добавление нового товара в корзину
/** @var CartItem $newItem */
$newItem = $this->model->create(
[
'code' => $this->code,
'user_id' => $this->auth->user()->getAuthIdentifier(),
'product_id' => $product->getKey(),
'quantity' => $quantity,
]
);
$this->clearCounters();
return $newItem;
}
public function deleteItem($condition)
{
if (!is_array($condition)) {
$condition = [ 'product_id' => $condition ];
}
$this->model->whereCode($this->code)->where($condition)->delete();
$this->clearCounters();
}
private function createNewCart()
{
$this->code = str_random();
$this->session->set('cart.code', $this->code);
$this->clearCounters();
}
private function clearCounters()
{
$this->session->remove('cart.count');
$this->session->remove('cart.total');
}
}
app/CartItem.php:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\CartItem
*
* @property integer $id
* @property string $code
* @property integer $user_id
* @property integer $product_id
* @property integer $quantity
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property-read \App\User $user
* @property-read \App\Product $product
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereId($value)
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereCode($value)
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereProductId($value)
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereQuantity($value)
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\App\CartItem whereUpdatedAt($value)
*/
class CartItem extends Model {
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts = [ 'quantity' => 'integer' ];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [ 'code', 'user_id', 'product_id', 'quantity' ];
public function user()
{
return $this->belongsTo('App\User');
}
public function product()
{
return $this->belongsTo('App\Product');
}
}
app/Product.php:
<?php namespace App;
use App\Events\CatalogWasChanged;
use Illuminate\Database\Eloquent\Model;
/**
* App\Product
*
* @property integer $id
* @property string $name
* @property string $photo
* @property string $sticker
* @property string $description
* @property string $properties
* @property float $price
* @property float $old_price
* @property integer $quantity
* @property boolean $published
* @property boolean $archived
* @property integer $top_position
* @property integer $updated_by
* @property string $updated_ip
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\ProductCategory[] $categories
* @property-read \App\User $editor
* @property-read \Illuminate\Database\Eloquent\Collection|\App\User[] $likes
* @method static \Illuminate\Database\Query\Builder|\App\Product whereId($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereName($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product wherePhoto($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereSticker($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereProperties($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product wherePrice($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereOldPrice($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereQuantity($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product wherePublished($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereArchived($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereTopPosition($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereUpdatedBy($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereUpdatedIp($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\App\Product whereUpdatedAt($value)
* @method static \App\Product available()
*/
class Product extends Model {
/**
* The attributes that should be casted to native types.
*
* @var array
*/
protected $casts = [
'published' => 'boolean',
'archived' => 'boolean',
'properties' => 'object',
'sort_order' => 'integer',
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'photo',
'sticker',
'description',
'properties',
'price',
'old_price',
'quantity',
'published',
'archived',
'sort_order',
'updated_by',
'updated_ip'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [ 'updated_ip' ];
public static function boot()
{
parent::boot();
static::created(function () { event(new CatalogWasChanged); });
static::saved(function () { event(new CatalogWasChanged); });
}
public function categories()
{
return $this->belongsToMany('App\ProductCategory');
}
public function editor()
{
return $this->belongsTo('App\User', 'updated_by');
}
public function likes()
{
return $this->belongsToMany('App\User', 'product_user_likes');
}
public function scopeAvailable($query)
{
return $query->wherePublished(true)->whereArchived(false)->where('quantity', '>', 0);
}
}
сам разберёшься? миграции для cart_items и products сам напишешь?
Не в сети
сам разберёшься? миграции для cart_items и products сам напишешь?
Спасибо огромное!
Мысль уловил, направление понял.
Буду пробовать сам, иначе не научусь.
Если уж совсем непонятно будет, спрошу совета.
Еще раз спасибо!
Не в сети
сам разберёшься? миграции для cart_items и products сам напишешь?
Подскажите, создал файл миграции 2017_03_20_120058_create_table_products.php
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string ('name',100);
$table->string ('photo',100);
$table->string ('sticker',100);
$table->string ('description',100);
$table->string ('properties',100);
$table->string ('price',100);
$table->string ('old_price',100);
$table->string ('quantity',100);
$table->string ('published',100);
$table->string ('archived',100);
$table->string ('sort_order',100);
$table->string ('updated_by',100);
$table->string ('updated_ip',100);
$table->timestamps();
});
}
А вот что создавать во второй миграции 2017_03_20_115759_create_table_cart_items.php, я не совсем понимаю, что-то такое?
public function up()
{
Schema::create('cart_items', function (Blueprint $table) {
$table->increments('id');
$table->string ('name',100);
$table->integer('id',100);
$table->string ('code',100);
$table->integer ('user_id',100);
$table->integer ('product_id',100);
$table->integer ('quantity',100);
$table->timestamps();
});
}
Я по правильному пути иду?
Не в сети
ну примерно. у меня свойства товаров определяются ТЗ, там есть вещи, которые тебе наверное не понадобятся. например я сохраняю кто из менеджеров последним менял свойства товара и с какого айпишника – достаточно специфичная задача. то же самое с архивом товаров, «зачёркнутой» ценой и сортировкой с админки вручную. начни с простого: name, quantity, price, а остальное добавляй по необходимости. в корзине ключом является рандомный код, которых сохранён в сессии, но при этом я запоминаю владельца корзины – это тоже кстати опциональный функционал – мне он нужен был для страницы с «брошенными корзинами», которые потом обрабатывают менеджеры и «добивают» клиентов. у меня миграция выглядит так:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartItemsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_items', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('code', 16)->index();
$table->integer('user_id')->unsigned();
$table->integer('product_id')->unsigned();
$table->integer('quantity')->unsigned();
$table->timestamps();
});
Schema::table('cart_items', function(Blueprint $table)
{
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('cart_items');
}
}
внешние ключи удаляют записи из таблицы при удалении товаров или пользователей, тоже кстати на всякий случай добавлено – на самом деле у меня ни товары ни пользователи никогда полностью не удаляются, товары уходят в архив, а пользователи удаляются мягко, через трест SoftDeletes. кроме того, у меня достаточно старая версия ларавеля в этом проекте (5.0), в ней нет ->unsignedInteger(), приходится писать явно
Не в сети
Мне действительно много из этого не нужно. Только самый простой вариант: name, quantity, price. Использую версию Laravel 5.2. Спасибо за помощь!
Не в сети
Использую версию Laravel 5.2. Спасибо за помощь!
Полез в файл routes.php и не нашел его, и вспомнил, что обновлялся до 5.4.6.
Возник вопрос, вот я попытался прописать маршруты в файле web, для: Cart.php, Product.php, CartItem.php и задумался.
Раньше я прописывал группу маршрутов контроллерам, которые делают ту или иную операцию, и вынесены они отдельно в файлы.
Например (упрощенно) файл web для Portfolio:
Route::group(['prefix'=>'portfolios'],function() {
Route::get('/',['uses'=>'PortfoliosController@execute','as'=>'portfolio']);
Route::match(['get','post'],'/add',['uses'=>'PortfoliosAddController@execute','as'=>'portfoliosAdd']);
Route::match(['get','post','delete'],'/edit/{portfolio}',['uses'=>'PortfoliosEditController@execute','as'=>'portfoliosEdit']);
});
Соответственно в IndexController.php контролере:
$portfolios = Portfolio::get(array('name','filter','images','price'));
В Portfolio.php:
class Portfolio extends Model
{
protected $fillable = ['name','filter','images','price'];
}
И для админки PortfoliosController.php, PortfoliosAddController.php, PortfoliosEditController.php с функциями добавления,редактирования,удаления.
А значит по аналогии, мне нужно зарегистрировать в web: Cart.php, Product.php, CartItem.php. Затем закомментировать не используемые мною функции, типа "проверка наличия на складе" в Cart.php. Создать кнопку корзина. Создать условие для клика мышкой на картинку, для помещения этой позиции в корзину, В корзине выставить нужное кол-во, и ... А тут моя фантазия закончилась. По идее, содержимое корзины нужно вывести в файл(.xls), и отправить заказ владельцу сайта.
Я вообще правильно рассуждаю? А то может вообще пошел огородами...
Не в сети
зачем в маршрутах регистрировать модели? что-то и правда огородами )
Не в сети
зачем в маршрутах регистрировать модели? что-то и правда огородами )
Действительно зачем? Я невнимателен. Не обратил внимания на подключение Eloquent. Спасибо!
Не в сети
зачем в маршрутах регистрировать модели? что-то и правда огородами )
Добрый день!
Я не правильно написал, регистрировать в маршрутах нужно шаблон корзины. Мне не понятен был сам механизм (цепочка) работы корзины.
Размышляя, что такое корзина - это страница (модальное окошко) на которой выводится информация из сессии. Т.е. пользователь выбрав товар - нажав на картинку (ссылку) кладет его в корзину -сессия записывается в базу (отрабатывает модель). Открывая же страничку корзины, из базы в нее вытаскиваются данные (массив данных из модели). Но Вы прислали три модели. И я не совсем понимаю назначение третьей. Cart.php, Product.php, CartItem.php - уточните пожалуйста назначение третьей модели.
Не в сети
Cart – это не модель, это сервис уровня приложения, который обеспечивает апи для работы с корзиной в коде сайта. он существует как раз для того чтобы с CartItem-ами не приходилось работать «руками». в сессии находится только идентификатор корзины, её содержимое как раз определяется записями в таблице cart_items, а модель CartItem – её представление в элоквент.
учитывая задаваемые вопросы, могу посоветовать либо вдумчиво почитать документацию, либо найти специалиста, который напишет сайт за деньги. меня находить не надо – я и так на двух работах работаю без выходных уже
Не в сети
Cart – это не модель, это сервис уровня приложения, который обеспечивает апи для работы с корзиной в коде сайта. он существует как раз для того чтобы с CartItem-ами не приходилось работать «руками». в сессии находится только идентификатор корзины, её содержимое как раз определяется записями в таблице cart_items, а модель CartItem – её представление в элоквент.
учитывая задаваемые вопросы, могу посоветовать либо вдумчиво почитать документацию, либо найти специалиста, который напишет сайт за деньги. меня находить не надо – я и так на двух работах работаю без выходных уже
Спасибо за ответ. Извиняюсь за беспокойство. Я новичок в Laravel, о чем писал в первом посте. Учусь, поставил себе задачу, и пытаюсь грызть гранит науки. Как оказалось, это не такая простая задача. Еще раз спасибо, и желаю успеха в работе.
Не в сети
Не стал заморачиваться с корзиной и установил пакет для Laravel! Что ставить?! Поищите в поиске: shopping cart laravel. В итоге имеем много различных корзин - например, одну для откладывания товара (для сравнения) вторую для шопинга (именно корзину).
Не в сети
Спасибо за совет! Сам я, за это время, просмотрел несколько видео уроков, и в каждом делали корзину на loravel. Но мой сайт не состоит из одной страницы (как в уроках), в нем каркас, и виды. А значит не совпадают маршруты к контролерам и т.д. Я не понимаю, как работают разные методы, например execut, и сижу, вот изучаю маршрутизацию. До этого у меня не было понимания, что представляет из себя корзина. Оказывается есть множество способов реализации на Laravel, такие как сессии, куки, сохранение в БД. Или реализация например на JS, с отправкой на AJAX. Но главное я понял, какие шаги нужно сделать последовательно для её создания.
Не в сети
Страницы 1