Esta es una copia de LaravelShoppingcart de bumbummen99, extendida con características menores compatibles con Laravel 11+. Un ejemplo de integración se puede encontrar aquí.
Instala el paquete a través de Composer.
Ejecuta el comando Composer require
desde la Terminal::
composer require anayarojo/shoppingcart
Ahora estás listo para comenzar a usar el Laravel Shopping Cart en tu aplicación.
A partir de la versión 2 de este paquete, es posible utilizar la inyección de dependencias para inyectar una instancia de la clase Cart en tu controlador u otra clase.
Definitivamente deberías publicar el archivo de config
y echarle un vistazo..
php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config"
Esto te proporcionará un archivo de configuración cart.php
en el cual puedes realizar cambios en el comportamiento del paquete."
As of version 4.2.0 this package does, when being used with PostgreSQL, encode the cart content to base64 before storing into database due to an issue with saving values including zero bytes. Please consider clearing your cart table in case you are upgrading using PostgreSQL from a version <4.2.0.
A partir de la versión 4.2.0, este paquete, al ser utilizado con PostgreSQL, codifica el contenido del carrito a base64 antes de almacenarlo en la base de datos debido a un problema al guardar valores que incluyen bytes nulos. Por favor, considera limpiar la tabla de tu carrito en caso de que estés realizando una actualización utilizando PostgreSQL desde una versión <4.2.0.
Consulta uno de los siguientes temas para obtener más información sobre LaravelShoppingcart
- Nota importante
- Uso
- Colecciones
- Instancias
- Modelos
- Base de Datos
- Calculadoras
- Excepciones
- Eventos
- Ejemplo
- Colaboradores
Como todos los carritos de compras que calculan precios incluyendo impuestos y descuentos, este módulo también podría verse afectado por el "problema de redondeo de totales" (*) debido a la precisión decimal utilizada para los precios y para los resultados. Con el fin de evitar (o al menos minimizar) este problema, en el paquete Laravel shoppingcart los totales se calculan utilizando el método "por fila" y se devuelven ya redondeados en función del formato numérico establecido por defecto en el archivo de configuración (cart.php). Debido a esto, DESACONSEJAMOS ESTABLECER ALTA PRECISIÓN COMO VALOR POR DEFECTO Y FORMATEAR EL RESULTADO DE SALIDA UTILIZANDO MENOS DECIMALES. Hacer esto puede llevar al problema de redondeo.
El precio base (precio del producto) se deja sin redondear.
El carrito de compras te proporciona los siguientes métodos para usar:
Agregar un artículo al carrito es realmente simple, solo usas el método add()
, el cual acepta una variedad de parámetros.
En su forma más básica, puedes especificar el id, nombre, cantidad, precio y peso del producto que deseas agregar al carrito.
Cart::add('293ad', 'Product 1', 1, 9.99, 550);
Como un quinto parámetro opcional, puedes pasarle opciones, de modo que puedas agregar múltiples elementos con el mismo id, pero con (por ejemplo) un tamaño diferente.
Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']);
El método add()
devolverá una instancia de CartItem del elemento que acabas de agregar al carrito.
¿Quizás prefieras agregar el artículo usando un array? Siempre y cuando el array contenga las claves requeridas, puedes pasarlo al método. La clave de opciones es opcional.
Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]);
Novedad en la versión 2 del paquete es la posibilidad de trabajar con la interfaz Buyable. La forma en que esto funciona es que tienes un modelo que implementa la interfaz Buyable, lo que te obliga a implementar algunos métodos para que el paquete sepa cómo obtener el id, nombre y precio de tu modelo. De esta manera, simplemente puedes pasar al método add()
un modelo y la cantidad, y automáticamente lo agregará al carrito.
Como bono adicional, asociará automáticamente el modelo con el CartItem.
Cart::add($product, 1, ['size' => 'large']);
As an optional third parameter you can add options.
Cart::add($product, 1, ['size' => 'large']);
Finalmente, también puedes agregar varios elementos al carrito a la vez.
Simplemente puedes pasar al método add()
un array de arrays, o un array de Buyables y serán agregados al carrito.
Cuando agregas varios elementos al carrito, el método add()
devolverá un array de CartItems.
Cart::add([
['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550],
['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']]
]);
Cart::add([$product1, $product2]);
Para actualizar un elemento en el carrito, primero necesitarás el rowId del elemento.
Luego puedes usar el método update()
para actualizarlo.
Si simplemente deseas actualizar la cantidad, pasarás al método de actualización el rowId y la nueva cantidad:
$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
Cart::update($rowId, 2); // Se actualizará la cantidad
Si deseas actualizar las opciones de un artículo dentro del carrito,
$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
Cart::update($rowId, ['options' => ['size' => 'small']]); // Se actualizará la opción de tamaño con un nuevo valor
Si deseas actualizar más atributos del artículo, puedes pasar al método de actualización un array o un Buyable
como segundo parámetro. De esta manera, puedes actualizar toda la información del artículo con el rowId proporcionado.
Cart::update($rowId, ['name' => 'Product 1']); // Se actualizará el nombre
Cart::update($rowId, $product); // Se actualizará el id, el nombre y el precio
Para eliminar un elemento del carrito, nuevamente necesitarás el rowId. Este rowId lo pasas simplemente al método remove()
y eliminará el elemento del carrito.
$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
Cart::remove($rowId);
Si deseas obtener un artículo del carrito utilizando su rowId, simplemente llama al método get()
en el carrito y pásale el rowId.
$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
Cart::get($rowId);
Por supuesto, también querrás obtener el contenido del carrito. Aquí es donde utilizarás el método content
. Este método devolverá una Colección de CartItems que puedes iterar y mostrar el contenido a tus clientes.
Cart::content();
Este método devolverá el contenido de la instancia actual del carrito. Si deseas el contenido de otra instancia, simplemente encadena las llamadas.
Cart::instance('wishlist')->content();
Si deseas eliminar completamente el contenido de un carrito, puedes llamar al método destroy
en el carrito. Esto eliminará todos los CartItems del carrito para la instancia actual del carrito.
Cart::destroy();
El método weight()
se puede utilizar para obtener el peso total de todos los elementos en el carrito, dado su peso y cantidad.
Cart::weight();
El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales.
Cart::weight($decimals, $decimalSeperator, $thousandSeperator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
Si no estás utilizando el Facade, sino que utilizas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad total $cart->weight
El método total()
se puede utilizar para obtener el total calculado de todos los elementos en el carrito, dado su precio y cantidad.
Cart::total();
El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales.
Cart::total($decimals, $decimalSeparator, $thousandSeparator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
Si no estás utilizando el Facade, sino que utilizas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad total $cart->total
El método tax()
se puede utilizar para obtener el monto calculado de impuestos para todos los elementos en el carrito, dado su precio y cantidad.
Cart::tax();
El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales.
Cart::tax($decimals, $decimalSeparator, $thousandSeparator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
Si no estás utilizando el Facade, sino que utilizas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad de impuestos $cart->tax
El método subtotal()
se puede utilizar para obtener el total de todos los elementos en el carrito, menos el monto total de impuestos.
Cart::subtotal();
El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales.
Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
Si no estás utilizando el Facade, sino que usas inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad subtotal $cart->subtotal
El método discount()
se puede utilizar para obtener el descuento total de todos los elementos en el carrito.
Cart::discount();
El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales.
Cart::discount($decimals, $decimalSeparator, $thousandSeparator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
Si no estás utilizando el Facade, sino que usas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad de descuento $cart->discount
El método initial()
se puede utilizar para obtener el precio total de todos los elementos en el carrito antes de aplicar descuentos e impuestos.
Podría ser obsoleto en el futuro. Cuando se redondee, podría verse afectado por el problema de redondeo, úsalo con cuidado o utiliza Cart::priceTotal().
Cart::initial();
El método formateará automáticamente el resultado, el cual puedes ajustar utilizando los tres parámetros opcionales.
Cart::initial($decimals, $decimalSeparator, $thousandSeparator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
El método priceTotal()
se puede utilizar para obtener el precio total de todos los elementos en el carrito antes de aplicar descuentos e impuestos.
Cart::priceTotal();
El método devuelve el resultado redondeado basado en el formato numérico predeterminado, pero puedes ajustarlo utilizando los tres parámetros opcionales.
Cart::priceTotal($decimals, $decimalSeparator, $thousandSeparator);
Puedes establecer el formato numérico predeterminado en el archivo de configuración.
Si no estás utilizando el Facade, sino que usas inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad subtotal $cart->initial
Si deseas saber cuántos artículos hay en tu carrito, puedes usar el método count()
. Este método devolverá el número total de artículos en el carrito. Entonces, si has agregado 2 libros y 1 camisa, devolverá 3 artículos.
Cart::count();
$cart->count();
Para encontrar un elemento en el carrito, puedes usar el método search()
.
Este método fue cambiado en la versión 2
Detrás de escena, el método simplemente utiliza el método de filtro de la clase Laravel Collection. Esto significa que debes pasarle un Closure en el cual especificarás tus términos de búsqueda.
Si, por ejemplo, deseas encontrar todos los elementos con un id de 1:
$cart->search(function ($cartItem, $rowId) {
return $cartItem->id === 1;
});
Como puedes ver, el Closure recibirá dos parámetros. El primero es el CartItem para realizar la verificación. El segundo parámetro es el rowId de este CartItem.
El método devolverá una Colección que contiene todos los CartItems que se encontraron
Esta forma de búsqueda te brinda un control total sobre el proceso de búsqueda y te da la capacidad de crear búsquedas muy precisas y específicas.
Puedes usar el método setTax()
para cambiar la tasa impositiva que se aplica al CartItem. Esto sobrescribirá el valor establecido en el archivo de configuración.
Cart::setTax($rowId, 21);
$cart->setTax($rowId, 21);
Puedes usar el método setGlobalTax()
para cambiar la tasa impositiva para todos los elementos en el carrito. Los nuevos elementos también recibirán el impuesto global establecido.
Cart::setGlobalTax(21);
$cart->setGlobalTax(21);
Puedes usar el método setGlobalDiscount()
para cambiar la tasa de descuento para todos los elementos en el carrito. Los nuevos elementos también recibirán el descuento establecido.
Cart::setGlobalDiscount(50);
$cart->setGlobalDiscount(50);
Puedes usar el método setDiscount()
para cambiar la tasa de descuento que se aplica a un CartItem. Ten en cuenta que este valor se cambiará si estableces el descuento global para el Carrito posteriormente.
Cart::setDiscount($rowId, 21);
$cart->setDiscount($rowId, 21);
Para la conveniencia de agregar rápidamente elementos al carrito y su asociación automática, tu modelo debe implementar la interfaz Buyable
. Puedes usar el trait CanBeBought
para implementar los métodos requeridos, pero ten en cuenta que estos utilizarán campos predefinidos en tu modelo para los valores requeridos.
<?php
namespace App\Models;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable {
use Gloudemans\Shoppingcart\CanBeBought;
}
Si el trait no funciona en el modelo o deseas mapear los campos manualmente, el modelo debe implementar los métodos de la interfaz Buyable
. Para hacerlo, debe implementar dichas funciones:
public function getBuyableIdentifier(){
return $this->id;
}
public function getBuyableDescription(){
return $this->name;
}
public function getBuyablePrice(){
return $this->price;
}
public function getBuyableWeight(){
return $this->weight;
}
Ejemplo:
<?php
namespace App\Models;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable {
public function getBuyableIdentifier($options = null) {
return $this->id;
}
public function getBuyableDescription($options = null) {
return $this->name;
}
public function getBuyablePrice($options = null) {
return $this->price;
}
public function getBuyableWeight($options = null) {
return $this->weight;
}
}
En múltiples instancias, el Carrito te devolverá una Colección. Esta es simplemente una Colección de Laravel, por lo que todos los métodos que puedes llamar en una Colección de Laravel también están disponibles en el resultado.
Como ejemplo, puedes obtener rápidamente el número de productos únicos en un carrito:
Cart::content()->count();
O puedes agrupar el contenido por el id de los productos:
Cart::content()->groupBy('id');
El paquete soporta múltiples instancias del carrito. La forma en que funciona es la siguiente:
Puedes establecer la instancia actual del carrito llamando a Cart::instance('nuevaInstancia')
. A partir de este momento, la instancia activa del carrito será nuevaInstancia, por lo que cuando agregues, elimines o obtengas el contenido del carrito, estarás trabajando con la instancia nuevaInstancia
del carrito.
Si deseas cambiar de instancia, simplemente llama nuevamente a Cart::instance('otraInstancia')
, y estarás trabajando con la otraInstancia
nuevamente.
Así que un pequeño ejemplo:
Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550);
// Get the content of the 'shopping' cart
Cart::content();
Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']);
// Get the content of the 'wishlist' cart
Cart::content();
// If you want to get the content of the 'shopping' cart again
Cart::instance('shopping')->content();
// And the count of the 'wishlist' cart again
Cart::instance('wishlist')->count();
También puedes usar el contrato InstanceIdentifier
para extender un Modelo deseado y asignar/crear una instancia de Carrito para él. Esto también permite establecer directamente el descuento global.
<?php
namespace App;
...
use Illuminate\Foundation\Auth\User as Authenticatable;
use Gloudemans\Shoppingcart\Contracts\InstanceIdentifier;
class User extends Authenticatable implements InstanceIdentifier
{
...
/**
* Get the unique identifier to load the Cart from
*
* @return int|string
*/
public function getInstanceIdentifier($options = null)
{
return $this->email;
}
/**
* Get the unique identifier to load the Cart from
*
* @return int|string
*/
public function getInstanceGlobalDiscount($options = null)
{
return $this->discountRate ?: 0;
}
}
// Inside Controller
$user = \Auth::user();
$cart = Cart::instance($user);
N.B. Ten en cuenta que el carrito permanece en la última instancia establecida durante todo el tiempo que no establezcas una diferente durante la ejecución del script.
N.B.2 La instancia de carrito predeterminada se llama default
, por lo que cuando no estás usando instancias, Cart::content();
es lo mismo que Cart::instance('default')->content()
.
Porque puede ser muy conveniente poder acceder directamente a un modelo desde un CartItem, es posible asociar un modelo con los elementos en el carrito. Digamos que tienes un modelo Product
en tu aplicación. Con el método associate()
, puedes decirle al carrito que un elemento en el carrito está asociado al modelo Product
.
De esta manera, ¡puedes acceder a tu modelo directamente desde el CartItem
!
El modelo se puede acceder a través de la propiedad model
en el CartItem.
Si tu modelo implementa la interfaz Buyable
y utilizaste tu modelo para agregar el elemento al carrito, se asociará automáticamente.
Aquí tienes un ejemplo:
// Primero agregaremos el elemento al carrito.
$cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']);
// A continuación, asociamos un modelo con el elemento.
Cart::associate($cartItem->rowId, 'Product');
// ¡O aún más fácil, llama al método associate en el CartItem!
$cartItem->associate('Product');
// Incluso puedes hacerlo en una sola línea
Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product');
// Ahora, al iterar sobre el contenido del carrito, puedes acceder al modelo.
foreach(Cart::content() as $row) {
echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.';
}
Para guardar el carrito en la base de datos para poder recuperarlo más tarde, el paquete necesita saber qué conexión de base de datos usar y cuál es el nombre de la tabla.
Por defecto, el paquete utilizará la conexión de base de datos predeterminada y usará una tabla llamada shoppingcart
. Puedes cambiar eso en la configuración.
php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations"
Esto colocará un archivo de migración de la tabla shoppingcart
en el directorio database/migrations
. Ahora todo lo que tienes que hacer es ejecutar php artisan migrate
para migrar tu base de datos.
Para almacenar tu instancia de carrito en la base de datos, debes llamar al método store($identifier)
. Donde $identifier
es una clave aleatoria, por ejemplo, el id o nombre de usuario del usuario.
Cart::store('username');
// Para almacenar una instancia de carrito llamada 'wishlist'
Cart::instance('wishlist')->store('username');
Si deseas recuperar el carrito de la base de datos y restaurarlo, todo lo que tienes que hacer es llamar al método restore($identifier)
donde $identifier
es la clave que especificaste para el método store.
Cart::restore('username');
// Para restaurar una instancia de carrito llamada 'wishlist'
Cart::instance('wishlist')->restore('username');
Si deseas fusionar el carrito con otro de la base de datos, todo lo que tienes que hacer es llamar al método merge($identifier)
donde $identifier
es la clave que especificaste para el método store
. También puedes definir si deseas mantener los descuentos y las tasas de impuestos de los artículos y si deseas enviar eventos "cart.added".
// Fusionar el contenido de 'savedcart' en 'username'.
Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, $dispatchAdd, 'savedcartinstance');
Si deseas borrar el carrito de la base de datos, todo lo que tienes que hacer es llamar al método erase($identifier)
donde $identifier
es la clave que especificaste para el método store
.
Cart::erase('username');
// Para borrar un carrito cambiando a una instancia llamada 'wishlist'
Cart::instance('wishlist')->erase('username');
La lógica de cálculo para el paquete está implementada y definida en clases Calculator
. Estas implementan el contrato Gloudemans\Shoppingcart\Contracts\Calculator
y determinan cómo se calculan y redondean los precios. Las calculadoras se pueden configurar en el archivo de configuración. Esta es la calculadora por defecto:
<?php
namespace Gloudemans\Shoppingcart\Calculation;
use Gloudemans\Shoppingcart\CartItem;
use Gloudemans\Shoppingcart\Contracts\Calculator;
class DefaultCalculator implements Calculator
{
public static function getAttribute(string $attribute, CartItem $cartItem)
{
$decimals = config('cart.format.decimals', 2);
switch ($attribute) {
case 'discount':
return $cartItem->price * ($cartItem->getDiscountRate() / 100);
case 'tax':
return round($cartItem->priceTarget * ($cartItem->taxRate / 100), $decimals);
case 'priceTax':
return round($cartItem->priceTarget + $cartItem->tax, $decimals);
case 'discountTotal':
return round($cartItem->discount * $cartItem->qty, $decimals);
case 'priceTotal':
return round($cartItem->price * $cartItem->qty, $decimals);
case 'subtotal':
return max(round($cartItem->priceTotal - $cartItem->discountTotal, $decimals), 0);
case 'priceTarget':
return round(($cartItem->priceTotal - $cartItem->discountTotal) / $cartItem->qty, $decimals);
case 'taxTotal':
return round($cartItem->subtotal * ($cartItem->taxRate / 100), $decimals);
case 'total':
return round($cartItem->subtotal + $cartItem->taxTotal, $decimals);
default:
return;
}
}
}
El paquete Cart lanzará excepciones si algo sale mal. De esta manera, es más fácil depurar tu código utilizando el paquete Cart o manejar el error según el tipo de excepciones. El paquete Cart puede lanzar las siguientes excepciones:
Excepción | Razón |
---|---|
CartAlreadyStoredException | Cuando se intenta almacenar un carrito que ya fue almacenado usando el identificador especificado |
InvalidRowIDException | Cuando el rowId que se pasó no existe en la instancia actual del carrito |
UnknownModelException | Cuando intentas asociar un modelo que no existe a un CartItem. |
The cart also has events build in. There are five events available for you to listen for.
Evento | Se activa | Parámetro |
---|---|---|
cart.adding | Cuando se agrega un elemento al carrito. | El CartItem que se está agregando. |
cart.updating | Cuando se actualiza un elemento en el carrito. | El CartItem que se está actualizando. |
cart.removing | Cuando se elimina un elemento del carrito. | El CartItem que se está eliminando. |
cart.added | Cuando se ha agregado un elemento al carrito. | El CartItem que se agregó. |
cart.updated | Cuando se ha actualizado un elemento en el carrito. | El CartItem que se actualizó. |
cart.removed | Cuando se ha eliminado un elemento del carrito. | El CartItem que se eliminó. |
cart.merged | Cuando se fusiona el contenido de un carrito. | - |
cart.stored | Cuando se ha almacenado el contenido de un carrito. | - |
cart.restored | Cuando se ha restaurado el contenido de un carrito. | - |
cart.erased | Cuando se ha borrado el contenido de un carrito. | - |
A continuación, te muestro un pequeño ejemplo de cómo listar el contenido del carrito en una tabla:
// Agrega algunos elementos en tu controlador.
Cart::add('192ao12', 'Product 1', 1, 9.99);
Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']);
// Muestra el contenido en una vista.
<table>
<thead>
<tr>
<th>Product</th>
<th>Qty</th>
<th>Price</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach(Cart::content() as $row) :?>
<tr>
<td>
<p><strong><?php echo $row->name; ?></strong></p>
<p><?php echo ($row->options->has('size') ? $row->options->size : ''); ?></p>
</td>
<td><input type="text" value="<?php echo $row->qty; ?>"></td>
<td>$<?php echo $row->price; ?></td>
<td>$<?php echo $row->total; ?></td>
</tr>
<?php endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan="2"> </td>
<td>Subtotal</td>
<td><?php echo Cart::subtotal(); ?></td>
</tr>
<tr>
<td colspan="2"> </td>
<td>Tax</td>
<td><?php echo Cart::tax(); ?></td>
</tr>
<tr>
<td colspan="2"> </td>
<td>Total</td>
<td><?php echo Cart::total(); ?></td>
</tr>
</tfoot>
</table>