Подтвердить что ты не робот

Laravel получить размер суммы, если не существует 0

Я хочу составить список со всеми доступными продуктами и размером за один размер, например

 name     | size1  | size2  | size 3 | size4   | total
 ________________________________________________________
 t-shirt1 |   0    |    1   |    3    |   9    | 13
 t-shirt2 |   3    |    1   |    1    |   9    | 14
 t-shirt3 |   0    |    0   |    0    |   0    | 0

Моя модель Product имеет отношение с ProductSize следующим образом:

public function sizeAvailable(){
    return $this->hasMany('App\ProductSize');

}

My ProductSize имеет отношение с Product как:

public function product()
{
    return $this->belongsTo('App\Product');
}

My ProductSize имеет отношение с Size как:

public function size(){
    return $this->belongsTo('App\Size');
}

Модель ProductSize содержит значение для суммы. Так что я могу получить все размеры для продукта с нужной суммой, а им также можно получить все продукты размером. Как создать список, как в моем примере. Можно ли использовать размер как индекс? поэтому я мог бы сделать (псевдокод), чтобы заполнить теги <td>:

for(all products)
{
    if(product[somesize] not null)
      {{ product[somesize]->amount }}
    else
       0
}

Проблема заключается в том, что я не знаю, как установить количество в <tr> таблицы, если есть, например, 1 размер (размер3), а другие нет, когда я делаю $product->sizeAvailable(), он будет содержать только 1 (размер3), поэтому мне нужно разместить 0 под size1, но как я могу проверить, не доступен ли размер1 для продукта? Или есть лучший способ заполнить суммы размера таблицы?

В настоящее время у меня есть:

 <table id="myTable" class="tablesorter table table-striped table-responsive">
        <thead>
        <tr>
            <th>
                Product
            </th>
            @foreach($sizes as $size)
                <th>
                    {{ $size->name }}
                </th>
            @endforeach
        </tr>
        </thead>
        <tbody>

            @foreach($products as $product)
                <tr >
                    <td>
                        {{ucfirst($product->name)}}
                    </td>
                    @foreach($sizes as $size)
                        <td>
                             HERE I NEED TO DO A CHECK IF $product HAS $size RETURN 0 ELSE RETURN ProductSize->amount
                        </td>
                    @endforeach



                </tr>
            @endforeach

        </tbody>
    </table>

Я мог бы сделать функцию в модели продукта, например:

 hasSize($size)
 {
    $result = select from ProductSize where size = $size  

    if ($result = null)
       return 0;

    return $result->amount();
 }

Но с этим решением я должен запустить запрос products * sizes раз, если у меня есть 1000 продуктов и 20 размеров, что бы запросить 20 000 запросов для одной таблицы.

____________________________
Relevant Database data
____________________________


products
-id           (primary key)
-name

sizes
-id           (primary key)
-name

product_sizes
-pr           (primary key)
-prodcut_id
-size_id
-amount
4b9b3361

Ответ 1

Вы создали вручную Многие для многих отношений, для которых Laravel уже предоставляет несколько полезных функций:

1. Обновить модель продукта (добавить или заменить существующее отношение):

public function sizes()
{
    // many to many relation with intermediate table column
    return $this->belongsToMany('App\Size', 'product_sizes')->withPivot('amount');
}

public function getTotalAttribute()
{
    // access pivot table directly and use sum aggregate
    return DB::table('product_sizes')->where('product_id', $this->attributes['id'])->sum('amount');
}

2. Извлеките модели (в контроллере) следующим образом:

$sizes = Size::all();
$products = Product::with('sizes')->get(); // eager load sizes

3. Шаблон клинка:

...
<td>
    {{ucfirst($product->name)}}
</td>
@foreach($sizes as $size)
<td>
    {{-- HERE I NEED TO DO A CHECK IF $product HAS $size RETURN 0 ELSE RETURN ProductSize->amount --}}
    {{-- check and retrieve by using Collection methods --}}
    {{ $product->sizes->contains($size) ? $product->sizes->find($size)->pivot->amount : 0 }}
</td>
@endforeach
<td>{{ $product->total }}</td>
....

После воссоздания вашего примера с приведенными выше изменениями и (очень маленьким) ядром базы данных я получаю:

Product     size1   size2   size3   total
T-shirt1    10      3       0       13
T-shirt2    5       0       0       5

  • По умолчанию это решение производит только 3 запроса из-за нетерпеливой загрузки. (Подтвердить с помощью DebugBar). Каждый расчет total добавляет один запрос (к сожалению, конструктор запросов Laravel не имеет агрегированных методов для сводных таблиц).

  • contains() и find() в представлении пересекают предварительно выбранные коллекции - здесь нет дополнительных запросов.

  • Вам не нужна модель ProductSize, так как она содержит только сводную таблицу с дополнительным атрибутом (amount). См. От многих до многих связей и получения промежуточных столбцов таблицы.

  • Для удобства: если таблица product_sizes переименована в product_size, и поэтому в соответствии с соглашением об именах таблиц Laravel второй параметр в $this->belongsToMany('App\Size', 'product_sizes') не нужен и может быть написан следующим образом: $this->belongsToMany('App\Size')

Таблица [product_size] выводится из алфавитного порядка связанных имен модели.

Ответ 2

Я не использую вашу структуру таблицы, потому что я не знаю ее точной конструкции, но я делаю для вас пример.

Я использую только 2 запроса, а не 20 000

Попробуйте, создайте новый дамп для использования базы данных

SQL dump

  # Host: localhost  (Version: 5.5.25)
  # Date: 2002-01-01 01:34:00
  # Generator: MySQL-Front 5.3  (Build 4.81)

  /*!40101 SET NAMES utf8 */;

  #
  # Structure for table "products"
  #

  DROP TABLE IF EXISTS `products`;
  CREATE TABLE `products` (
    `product_id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`product_id`)
  ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

  #
  # Data for table "products"
  #

  INSERT INTO `products` VALUES (1,'iphone'),(2,'Ls'),(3,'Samsung');

  #
  # Structure for table "size_name"
  #

  DROP TABLE IF EXISTS `size_name`;
  CREATE TABLE `size_name` (
    `Id` int(11) NOT NULL AUTO_INCREMENT,
    `size_id` varchar(255) DEFAULT NULL,
    `product_id` varchar(255) DEFAULT NULL,
    `name` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`Id`)
  ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

  #
  # Data for table "size_name"
  #

  INSERT INTO `size_name` VALUES (1,'1','2','color'),(2,'2','1','width'),(3,'3','3','height'),(6,'3','2','color'),(7,'2','3','width'),(8,'1','1','height'),(9,'1','3','color'),(10,'2','2','width'),(11,'3','1','height');

  #
  # Structure for table "size_value"
  #

  DROP TABLE IF EXISTS `size_value`;
  CREATE TABLE `size_value` (
    `Id` int(11) NOT NULL AUTO_INCREMENT,
    `value_name` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`Id`)
  ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

  #
  # Data for table "size_value"
  #

  INSERT INTO `size_value` VALUES (1,''),(2,'bluen'),(3,'green'),(5,'type2'),(6,'type3'),(7,'type4');

А теперь PHP

  <?php
  $bd_host="localhost"; 
  $bd_user="root";
  $bd_password="";
  $bd_base="newdatabase2";
  $con=mysql_connect($bd_host, $bd_user, $bd_password); 
  if(!con) { echo 'error.'; exit; }
  mysql_select_db($bd_base, $con); 
  mysql_query('SET NAMES utf8');
  /* query1 get table rows caption */
  $q="select * from size_name group by size_id";
  $res=mysql_query($q);
  while($row=mysql_fetch_assoc($res)) {
  $caption[$row['id']]=$row['name'];
  }
  // I limit to 3 products but you can change here, make loop
  $prod1='1';
  $prod2='2';
  $prod3='3';
  //here you need to change to work with all products
  $where=" where size_name.product_id='$prod1' or size_name.product_id='$prod2' or size_name.product_id='$prod3' ";
  /* query2 select size for your example */
  $q="select * from size_name left join (size_value) on (size_name.id=size_value.id ) $where order by size_id, product_id";
  $res=mysql_query($q);
  while($row=mysql_fetch_array($res)) {
  if(!isset($id) || $id<>$row['size_id']) $id=$row['size_id']; 

  /**/
  /* here nedd to make loop for all products youu need, I do for only 3 products */
  /**/
  /**/
  /*#1*/ 
  if($row['product_id']==$prod1) $characteristics1[$prod1][$id]=$row['value_name']; else
  /*#2*/
  if($row['product_id']==$prod2) $characteristics2[$prod2][$id]=$row['value_name']; else
  /*#3*/
  if($row['product_id']==$prod3) $characteristics3[$prod3][$id]=$row['value_name']; 
  /**/
  /**/
  } 
  ?>
  <table style="width:80%;border-spacing:0; text-align:center;">
  <tr style="background-color:purple;color:#FFF;">
  <td style="background-color:yellow; color:#999;"></td>
  <td>Product 1</td>
  <td>Product 2</td>
  <td>Product 3</td>
  </tr>
  <?
  foreach($caption as $key => $value) { ?>
  <tr>
  <td style="background-color:yellow;width:20%;"><? echo $caption[$key]; ?></td>
  <td style="width:20%;"><? if($characteristics1[$prod1][$key]!='') echo $characteristics1[$prod1][$key]; else echo ' 0 '; ?></td>
  <td style="width:20%;"><? if($characteristics2[$prod2][$key]!='') echo $characteristics2[$prod2][$key]; else echo ' 0 '; ?></td>
  <td style="width:20%;"><? if($characteristics3[$prod3][$key]!='') echo $characteristics3[$prod3][$key]; else echo ' 0 '; ?></td>
  <?
  } ?>
  </tr></table>