2016-02-03 7 views
13

chcę zrobić listę wszystkich dostępnych produktów oraz kwoty za rozmiarze jaklaravel dostać kwotę rozmiar jeśli nie istnieją 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 

My Product modelu ma związek z ProductSize jak:

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

} 

My ProductSize ma związek z Product jak:

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

My productSize ma związek z Size jak:

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

Model ProductSize zawiera wartość kwoty. Więc jestem w stanie uzyskać wszystkie rozmiary dla produktu o odpowiedniej ilości, a także jestem w stanie uzyskać wszystkie produkty od rozmiaru. Jak mogę utworzyć listę tak, jak w moim przykładzie. Czy można używać rozmiaru jako indeksu? więc mogłem zrobić (pseudo kod), aby wypełnić <td> tagi:

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

Problem mam to, że nie wiem jak ustawić kwotę w <tr> stołu jeśli jest na przykład 1 rozmiarze (size3) i żadnych innych, gdy robię $product->sizeAvailable() będzie zawierał tylko 1 wartość (size3), więc muszę umieścić 0 pod rozmiar1, ale jak mogę sprawdzić, czy rozmiar 1 nie jest dostępny dla produktu? Czy istnieje lepszy sposób na wypełnienie kwot wielkości tabeli?

Co Obecnie mam:

<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> 

mogę utworzyć funkcję w modelu produktu podobnego:

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

    if ($result = null) 
     return 0; 

    return $result->amount(); 
} 

Ale z tego rozwiązania muszę uruchomić kwerendę products * sizes razy, jeśli mam 1000 produkty i 20 rozmiarów, co dałoby mi 20 000 zapytań za jeden stół.

____________________________ 
Relevant Database data 
____________________________ 


products 
-id   (primary key) 
-name 

sizes 
-id   (primary key) 
-name 

product_sizes 
-pr   (primary key) 
-prodcut_id 
-size_id 
-amount 
+0

Czy bawiłeś się agregatami ([tutaj] (https://laravel.com/docs/5.2/eloquent#retrieving-aggregates) & [tutaj] (https://laravel.com/docs/5.2/queries # ordering-grouping-limit-and-offset)) dla tego problemu Sven? – Theson

+0

Muszę przyznać, że dołączenie do SQL lub nawet widok oparty na tym byłby dobrze dopasowany - wciąż próbowałem dostarczyć rozwiązanie oparte na całej Laravel, aby pokazać, jak daleko może się posunąć. – luchaos

+1

to "Właściwe dane z bazy danych" u dołu kompletne? wspomniałeś, że model ProductSizes zawiera wartość dla 'amount' - czy tabela' product_sizes' ma tę kolumnę? – luchaos

Odpowiedz

7

Masz ręcznie zbudował Many to Many Relationship, dla którego Laravel oferuje już kilka przydatnych Cechy:

1. Aktualizacja modelu produktu (dodanie lub zastąpienie istniejącej relacji):

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.Odzyskaj modele (kontroler) tak:

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

3. Blade szablon:

... 
<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> 
.... 

Po odtworzeniu swój przykład ze zmianami powyżej i (bardzo mały) nasion bazy Otrzymuję:

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

  • Domyślnie to rozwiązanie powoduje tylko 3 zapytania z powodu eager loading. (Zweryfikuj przy pomocy DebugBar) Każde obliczenie total dodaje jedno zapytanie (niestety konstruktor zapytań Laravel nie ma aggregate methods dla tabel przestawnych).

  • i find() w widoku przechodzenie do wstępnie pobranych kolekcji - bez dodatkowych zapytań tutaj.

  • Nie potrzebujesz modelu ProductSize, ponieważ zawiera on tylko tabelę przestawną z dodatkowym atrybutem (amount). Zobacz Many to Many relationships and retrieving intermediate table columns.

  • Dla wygody: jeśli tabela product_sizes zostaje zmieniona na product_size a zatem następująco konwencję stół nazewnictwa laravel za drugi parametr w $this->belongsToMany('App\Size', 'product_sizes') nie jest potrzebne i może być napisane tak: $this->belongsToMany('App\Size')

Sieć [ product_size] pochodzi z kolejności alfabetycznej powiązanych nazw modeli.

+0

@ ktoś: przedstawcie konstruktywny komentarz, jeśli głosowanie w dół, jak sugeruje SO - bez niego nie ma sensu. – luchaos

1

Nie używam twojej struktury stołu, ponieważ nie znam dokładnej konstrukcji, ale robię dla ciebie przykład.

używam tylko 2 queryes a nie 20 000

Spróbuj, zrobić nowe zastosowanie bazy zrzucić

SQL zrzutu

# 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'); 

a teraz 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>