Laravel模型属于关系加载问题

时间:2019-03-13 14:40:26

标签: laravel laravel-5.5

我有两个模型,Box和BoxLocations。 BoxhasManyBoxLocations关系,而BoxLocationsbelongsToBox关系。

BoxLocations还具有附加到模型的属性,该属性需要Box关系中的单个信息。

我注意到,在调用Box::with(['BoxLocations']->)all();时,我看到BoxLocations模型正在重新加载Box关系。每个BoxLocation(50次奇数次)都会发生这种情况

laravel是否不跟踪最初的Box::with(['BoxLocations']->)all();请求是否已加载Box,然后将其传递给BelongsTo关系?

我正在尝试优化Web系统,并在加载了take属性时(每次加载时同样需要它),对于已经加载的相同Box模型,它对数据库造成了50次奇怪的点击。

如果laravel不这样做-有更好的方法来实现上述目标吗?

1 个答案:

答案 0 :(得分:0)

当您使用GO /****** Object: Trigger [dbo].[Perfect_OJ] Script Date: 13/03/2019 13:11:06 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER TRIGGER [dbo].[Perfect_OJ] ON [dbo].[CharQuests] AFTER INSERT AS BEGIN SET NOCOUNT ON; DECLARE @QuestID INT = (SELECT QuestID FROM inserted) IF @QuestID = 2222 -- The Perfect Orange Stats QuestID BEGIN DECLARE @CharID INT = (SELECT CharID FROM inserted) DECLARE @UserUID INT = (SELECT UserUID FROM Chars WHERE CharID = @CharID) DECLARE @ItemType TINYINT, @RecRuneItemID INT DECLARE @StatRecRuneItemID TABLE (StatRecRuneItemID INT) -- Str, Dex, Rec, Int, Wis, Luc DECLARE @VitalRecRuneItemID TABLE (VitalRecRuneItemID INT) -- HP, MP, SP DECLARE @GearTypes TABLE (GearTypes TINYINT) -- Gears, Capes and Shields DECLARE @WeaponTypes TABLE (WeaponTypes TINYINT) -- Weapons and Accessories -- The above tables were created in order to ensure a fast access to the types -- of items that are to be recreated and the rune used to recreate them. SET @ItemType = (SELECT Type FROM UserStoredItems WHERE UserUID = @UserUID AND Slot = 0) SET @RecRuneItemID = (SELECT ItemID FROM UserStoredItems WHERE UserUID = @UserUID AND Slot = 1) INSERT INTO @StatRecRuneItemID VALUES (100230),(100231),(100232),(100233),(100234),(100235) -- ItemIDs of stat recreation runes (Str, Dex, Rec, Int, Wis, Luc, HP, MP, SP) INSERT INTO @VitalRecRuneItemID VALUES (100236),(100237),(100238) -- ItemIDs of vital stat recreation runes (HP, MP, SP) INSERT INTO @GearTypes VALUES (16),(17),(18),(19),(20),(21),(24),(31),(32),(33),(34),(35),(36),(39),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(22),(23),(40),(72),(73),(74),(76),(77),(87),(88),(89),(91),(92),(96),(97) -- Types of gears, capes. Here you can include anything you want to be rerollable with both stat and vital stat recreation runes. INSERT INTO @WeaponTypes VALUES (16),(17),(18),(19),(20),(21),(24),(31),(32),(33),(34),(35),(36),(39),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(22),(23),(40),(72),(73),(74),(76),(77),(87),(88),(89),(91),(92),(96),(97) -- Types of weapons, accessories. Here you can include anything you want to be rerollable only with stat recreation runes. IF ((@ItemType IN (SELECT GearTypes FROM @GearTypes)) AND (@RecRuneItemID IN (SELECT StatRecRuneItemID FROM @StatRecRuneItemID) OR @RecRuneItemID IN (SELECT VitalRecRuneItemID FROM @VitalRecRuneItemID))) OR (@ItemType IN (SELECT WeaponTypes FROM @WeaponTypes) AND (@RecRuneItemID IN (SELECT StatRecRuneItemID FROM @StatRecRuneItemID))) -- The above IF statement checks if the item to be recreated and the recreation rune are compatible with each other. -- As you can see, it works only if it's an item with the Type from @GearTypes(gears, capes) and all recreation runes, -- or if it's an item with the Type from @WeaponTypes(weapons, accessories) and stat recreation runes only. BEGIN DECLARE @Craftname VARCHAR(20), @Str TINYINT, @Dex TINYINT, @Rec TINYINT, @Int TINYINT, @Wis TINYINT, @Luc TINYINT, @HP TINYINT, @MP TINYINT, @SP TINYINT, @Enchant TINYINT, @ItemID INT, @ReqWis TINYINT, @Server TINYINT, @OJs TINYINT, @NewOJs TINYINT, @MaxedOJs TINYINT, @LowestOJType VARCHAR(3), @LowestOJ TINYINT SET @Craftname = (SELECT Craftname FROM UserStoredItems WHERE UserUID = @UserUID AND Slot = 0) SET @Str = CAST(SUBSTRING(@Craftname,1,2) AS TINYINT) SET @Dex = CAST(SUBSTRING(@Craftname,3,2) AS TINYINT) SET @Rec = CAST(SUBSTRING(@Craftname,5,2) AS TINYINT) SET @Int = CAST(SUBSTRING(@Craftname,7,2) AS TINYINT) SET @Wis = CAST(SUBSTRING(@Craftname,9,2) AS TINYINT) SET @Luc = CAST(SUBSTRING(@Craftname,11,2) AS TINYINT) SET @HP = CAST(SUBSTRING(@Craftname,13,2) AS TINYINT) SET @MP = CAST(SUBSTRING(@Craftname,15,2) AS TINYINT) SET @SP = CAST(SUBSTRING(@Craftname,17,2) AS TINYINT) SET @Enchant = CAST(SUBSTRING(@Craftname,19,2) AS TINYINT) SET @ItemID = (SELECT ItemID FROM UserStoredItems WHERE UserUID = @UserUID AND Slot = 0) SET @ReqWis = (SELECT ReqWis FROM PS_GameDefs.dbo.Items WHERE ItemID = @ItemID) -- Get the maximum possible OJ on the item SET @Server = (SELECT Server FROM PS_GameDefs.dbo.Items WHERE ItemID = @ItemID) -- Get the maximum amount of OJs on the item DECLARE @AllOJs TABLE (StatName VARCHAR(3), Stats TINYINT) INSERT INTO @AllOJs VALUES ('Str',@Str),('Dex',@Dex),('Rec',@Rec),('Int',@Int),('Wis',@Wis),('Luc',@Luc),('HP',@HP),('MP',@MP),('SP',@SP) -- Declaring the @AllOJs table and storing the OJs of the item before the recreation process. SET @OJs = (SELECT COUNT(*) FROM @AllOJs WHERE Stats > 0) -- Getting the amount of OJs SET @MaxedOJs = (SELECT COUNT(*) FROM @AllOJs WHERE Stats = @ReqWis) -- Getting the amount of MaxedOJs IF (@OJs <= @Server) AND (@MaxedOJs < @Server) -- Checking if there's still room for another perfect OJ. BEGIN DECLARE @NewStr TINYINT, @NewDex TINYINT, @NewRec TINYINT, @NewInt TINYINT, @NewWis TINYINT, @NewLuc TINYINT, @NewHP TINYINT, @NewMP TINYINT, @NewSP TINYINT, @NewEnchant TINYINT -- Declaring the new variables, where the new orange stats will be stored SET @NewStr = @Str SET @NewDex = @Dex SET @NewRec = @Rec SET @NewInt = @Int SET @NewWis = @Wis SET @NewLuc = @Luc SET @NewHP = @HP SET @NewMP = @MP SET @NewSP = @SP SET @NewEnchant = @Enchant -- Assigning the old OJs to the new OJ variables IF @RecRuneItemID = 100230 -- STR Rec Rune BEGIN SET @NewStr = @ReqWis END ELSE IF @RecRuneItemID = 100231 -- DEX Rec Rune BEGIN SET @NewDex = @ReqWis END ELSE IF @RecRuneItemID = 100232 -- REC Rec Rune BEGIN SET @NewRec = @ReqWis END ELSE IF @RecRuneItemID = 100233 -- INT Rec Rune BEGIN SET @NewInt = @ReqWis END ELSE IF @RecRuneItemID = 100234 -- WIS Rec Rune BEGIN SET @NewWis = @ReqWis END ELSE IF @RecRuneItemID = 100235 -- LUC Rec Rune BEGIN SET @NewLuc = @ReqWis END ELSE IF @RecRuneItemID = 100236 -- HP Rec Rune BEGIN SET @NewHP = @ReqWis END ELSE IF @RecRuneItemID = 100237 -- MP Rec Rune BEGIN SET @NewMP = @ReqWis END ELSE IF @RecRuneItemID = 100238 -- SP Rec Rune BEGIN SET @NewSP = @ReqWis END END ELSE GOTO FAIL END -- End of the case where both items are good and compatible with each other ELSE GOTO FAIL -- In the case where the item and the rec rune aren't compatible with each other, -- the process will end. No modifications to the item will occur. CHECKOJAMOUNT: -- This label checks if after the reroll there aren't more -- types of orange stats than the item can normally have. DECLARE @NewAllOJs TABLE (StatName VARCHAR(3), Stats TINYINT) INSERT INTO @NewAllOJs VALUES ('Str',@NewStr),('Dex',@NewDex),('Rec',@NewRec),('Int',@NewInt),('Wis',@NewWis),('Luc',@NewLuc),('HP',@NewHP),('MP',@NewMP),('SP',@NewSP) -- Declaring the @NewAllOJs table and assigning the new OJs to it. SET @NewOJs = (SELECT COUNT(*) FROM @NewAllOJs WHERE Stats > 0) -- Getting the amount of OJs on the item after the reroll. IF @NewOJs > @Server -- If the amount is bigger than what the item can normally have, -- the lowest stat, that is bigger than zero of course, is going to be removed. -- e.g. If the item allows max 4 orange stats, max stat being 40, -- it has 40 STR, 40 DEX, 40 REC, 39 LUC, and we want to use a HP Rec Rune on it. -- After the reroll process it will have 4000 HP, 40 STR, 40 DEX, 40 REC and 0 LUC. BEGIN SET @LowestOJType = (SELECT StatName FROM @NewAllOJs WHERE Stats = (SELECT MIN(Stats) FROM @NewAllOJs WHERE Stats > 0)) -- Getting the lowest stat. IF @LowestOJType = 'Str' SET @NewStr = 0 ELSE IF @LowestOJType = 'Dex' SET @NewDex = 0 ELSE IF @LowestOJType = 'Rec' SET @NewRec = 0 ELSE IF @LowestOJType = 'Int' SET @NewInt = 0 ELSE IF @LowestOJType = 'Wis' SET @NewWis = 0 ELSE IF @LowestOJType = 'Luc' SET @NewLuc = 0 ELSE IF @LowestOJType = 'HP' SET @NewHP = 0 ELSE IF @LowestOJType = 'MP' SET @NewMP = 0 ELSE IF @LowestOJType = 'SP' SET @NewSP = 0 -- Whichever the lowest stat is, it is changed to 0. END CRAFTNAMEUPDATE: -- This label is responsible for applying the new orange stat to the item. DECLARE @FinalStr VARCHAR(2), @FinalDex VARCHAR(2), @FinalRec VARCHAR(2), @FinalInt VARCHAR(2), @FinalWis VARCHAR(2), @FinalLuc VARCHAR(2), @FinalHP VARCHAR(2), @FinalMP VARCHAR(2), @FinalSP VARCHAR(2), @FinalEnchant VARCHAR(2) -- As Craftname is a varchar(20), we need to create it out of varchars. SET @FinalStr = CAST(@NewStr AS VARCHAR(2)) SET @FinalDex = CAST(@NewDex AS VARCHAR(2)) SET @FinalRec = CAST(@NewRec AS VARCHAR(2)) SET @FinalInt = CAST(@NewInt AS VARCHAR(2)) SET @FinalWis = CAST(@NewWis AS VARCHAR(2)) SET @FinalLuc = CAST(@NewLuc AS VARCHAR(2)) SET @FinalHP = CAST(@NewHP AS VARCHAR(2)) SET @FinalMP = CAST(@NewMP AS VARCHAR(2)) SET @FinalSP = CAST(@NewSP AS VARCHAR(2)) SET @FinalEnchant = CAST(@NewEnchant AS VARCHAR(2)) -- Assigning the new OJs to the respective varchar(2) variables. IF LEN(@FinalStr) < 2 BEGIN SET @FinalStr = '0' + @FinalStr END IF LEN(@FinalDex) < 2 BEGIN SET @FinalDex = '0' + @FinalDex END IF LEN(@FinalRec) < 2 BEGIN SET @FinalRec = '0' + @FinalRec END IF LEN(@FinalInt) < 2 BEGIN SET @FinalInt = '0' + @FinalInt END IF LEN(@FinalWis) < 2 BEGIN SET @FinalWis = '0' + @FinalWis END IF LEN(@FinalLuc) < 2 BEGIN SET @FinalLuc = '0' + @FinalLuc END IF LEN(@FinalHP) < 2 BEGIN SET @FinalHP = '0' + @FinalHP END IF LEN(@FinalMP) < 2 BEGIN SET @FinalMP = '0' + @FinalMP END IF LEN(@FinalSP) < 2 BEGIN SET @FinalSP = '0' + @FinalSP END IF LEN(@FinalEnchant) < 2 BEGIN SET @FinalEnchant = '0' + @FinalEnchant END -- The above 10 blocks are here to ensure that each orange stat varchar(2) is actually 2 digit long. UPDATE UserStoredItems SET Craftname = @FinalStr + @FinalDex + @FinalRec + @FinalInt + @FinalWis + @FinalLuc + @FinalHP + @FinalMP + @FinalSP + @FinalEnchant WHERE UserUID = @UserUID AND Slot = 0 -- The above block updates the Craftname of the item. DELETERUNE: -- This label removes the recreation rune. -- As you can see, it removes just one recreation rune, not the whole stack. UPDATE UserStoredItems SET Count = Count-1 WHERE UserUID = @UserUID AND Slot = 1 -- Decrementing the amount of recreation runes DELETE FROM UserStoredItems WHERE UserUID = @UserUID AND Slot = 1 AND Count = 0 -- If after decrementing the amount of recreation runes the amount is 0, delete the row from the database. END FAIL: -- This label is called from above a couple of times, -- in case the recreation requirements aren't met. DELETEQUEST: -- This label deletes the recreation quest. DELETE FROM CharQuests WHERE CharID = @CharID AND QuestID = @QuestID END ` 方法时,Laravel使用eager loading

  

当访问口才关系作为属性时,关系数据是“延迟加载”的。这意味着直到您首次访问该属性,关系数据才被实际加载。但是,Eloquent可以在查询父模型时“急于加载”关系。积极的加载减轻了N + 1查询问题。

因此,如果您这样做:

with()

它已经加载了关系,但是让我们说您这样做:

$boxes = Box::with('BoxLocations')->get();

如果您有50个Box,则此循环将运行51个查询。

但是当您使用$boxes = Box::all(); foreach($boxes as $box) { echo box->boxlocation->name; } 方法并渴望加载该关系时,此循环将仅运行2个查询。

您还可以使用Lazy Eager Loading来决定何时加载关系