Laravel 5.4 Model Properties and Methods Inheritance - php

The main model is BaseMonster, with all the basic informations of a monster (stats, monster type, etc) that will be inherited by child classes.
At the moment, I have two child classes named WildMonster and PlayerMonster, related to BaseMonster with a one to many relationship (from BaseMonster side).
My main goal is to be able to inherit attributes like stats, names and images and all the related methods from BaseMonster to other child monster classes.
This way i can avoid db redundancy for common attributes and at the same time adjust various aspect of certain child monsters just editing theirs parent BaseMonster record.
Example: "Kraken" monster has too high attack stat value. With a simple change to its BaseMonster record, all WildMonster and PlayerMonster instances related to "Kraken" are affected.
I want to avoid code redundancy too, for example making one rating() method in BaseMonster that will use all the actual stats (along with stat bonuses and levels) of the monster instance and that can be used by any of the monster child classes.
I tried accessing parent properties with Laravel accessors, and setting them to the Eloquent base_monster relationship stat, but this way i can't work with children attributes, like level or health_bonus to adjust values accordingly. (or at least i didn't find the right way).
I also tried setting accessors from child side with half a success, but anyway this mean i have to write the same accessors in all child classes and to make a huge mess if i want to change something.
How can I achieve something like this in laravel 5.4?
Extract of attributes
BaseMonster is the parent of the other monster classes and contains all the common attributes and methods needed in most of the occasions. BaseMonsters are created by Game Admin to manage things like stats and images and to generate new WildMonster to populate various Dungeons.
BaseMonster (extends Model)
name
image
health
attack
defense
monster_type_id (relationship with the MonsterType model)
WildMonster is the monster encountered in dungeons or in other ways. Can "transform" to PlayerMonster if recruited.
WildMonster (extends BaseMonster)
name (to customize name for special encounters)
level (to adjust stats accordingly)
health_bonus (there is a chance a monster has various stats bonuses)
attack_bonus
defense_bonus
base_monster_id (relationship with the BaseMonster model)
PlayerMonster is the monster recruited by the player after a battle.
PlayerMonster (extends BaseMonster)
name (eventually customized by the player)
level
health_bonus
attack_bonus
defense_bonus
base_monster_id (relationship with the BaseMonster model)

What you are looking for is called Polymorphic Relations in Laravel. This way you can define a base model BaseMonster as requested. Following the example on the site:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BaseMonster extends Model
{
public function monster_wild_or_player()
{
return $this->morphTo();
}
public function monster_type()
{
return $this->hasOne('App\MonsterType');
}
}
class PlayerMonster extends Model
{
public function base_info()
{
return $this->morphMany('App\BaseMonster', 'monster_wild_or_player');
}
}
class WildMonster extends Model
{
public function base_info()
{
return $this->morphMany('App\BaseMonster', 'monster_wild_or_player');
}
}

Related

Laravel Eager Loading, multiple same hasOne relations

I have 2 simple models. First, one is called Builds and the second one is called SlotOptions. Each build can have like 5 assigned slots.
class BuildDB extends Model
And has 5 such relations slot1-5 and id changes to slot1-5_id
public function slot1()
{
return $this->hasOne('\App\SlotOptions', 'id', 'slot1_id');
}
In the controller I call it such way;
BuildDB::with([ 'slot1', 'slot2', 'slot3', 'slot4', 'slot5'])->find(5);
\App\SlotOptions model doesn't contain any extra coding.
This generates 5 "same" queries. - atm the eager loading would work if I get a list of builds and each slot would have whereIn clause, is it possible to have it a one big wherein the clause, or does it require to change the DB schema.
It's not possible to optimize eager loading in this case.
I recommend that you change your database schema to a many-to-many relationship.
This design is more flexible, it allows you to easily add more slots in the future.
Create a pivot table named build_slot_option with these columns: build_id, slot_option_id
Add an additional column if you want to number/order the slots.
Then define a BelongsToMany relationship:
class BuildDB extends Model
{
public function slots()
{
return $this->belongsToMany(
SlotOptions::class, 'build_slot_option', 'build_id', 'slot_option_id'
);
}
}
BuildDB::with('slots')->find(5);

Different implementation of Eloquent instance for the same table

I'm currently working a tournament organization project, I would like to what's the best technique to have different implementation for the same model, for example I have model called matches, than retrieves data related to matches, but these matches may have different types depending on match_type field specified in matches model, what I did is creating parent class /Match , and having different types of matches extend this parent class. Ex:
Class SinglElimination/Match extends /Match{}
Class GroupStage/Match extends /Match{}
so with this design I have to get the parent match first to get the match_type then re-run the query to get a match with the needed child model
$match=Match::find($id);
$type = $match->match_type;
$childMatch = new $match_type.'/Match'();
$match = $match->where('_id', $this->_id)->first();
which I know is nowhere near clean, so how would you implement this ?
If I were you I would separate those 3 classes and exclude any extending. Take a look at Polymorphic relationships in Laravel, here is the quick link. It will be a cleaner approach and in my opinion it would be the best approach here, all you'll have to do is design the tables properly and do relationships properly too.

how to implement sub model in Laravel

my base class is post
and many submodel such as : video post , image post
all class have specific attribute & inherit parent attrib
& all class need specific behaviors
Problem
when find on post model elequent give super model(post) instance, its wrong
i need instance of submodel
If I understood you correctly, you need relationships
Add a hasMany relationship to your Post.php model:
public function videos()
return $this->hasMany(App\PostVideo::class);
}
As long as your post_video table has a post_id column that references a post, you can call this relationship like this:
foreach($post->videos as $video) {
// Do something
}
And the inverse relationship:
Add a relationship to your PostVideo.php model:
public function post() {
return $this->belongsTo(App\Post::class);
}
And of course, if you have a video, you can access the post it belongs to by doing:
$video->post
It is looked like you want a single table inheritance. In laravel this could be done manually or use package like nanigans or intrip. To use single table inheritance manually, i could suggest you start with reading this stackoverflow question first. However, notice that single table inheritance put everything in a single table but refered by several models that have different behavior. If this is not what you want, just use simple eloquent queries and models - which already explained by Pistachio.

Where to place a database query in model? (Symfony framework)

I'm new to PHP programing and Symfony. English isn't my native language so sorry if it sounds strange how I write...
I have two entities: Article and Category, where each Article has a Category. And I need to show how many articles I have for a given category:
Category ------------------ N°
Vehicles -------------------- 4
Electronics ---------------- 20
Food ----------------------- 15
Furniture ------------------ 8
With Doctrine I've made the CRUD files for both entities.
php app/console doctrine:generate:crud
Like I said, the problem is that I want to show a table with properties of a Category (name, description, etc) and how many articles of it are in the inventory.
The SQL query is very simple:
SELECT count(*) FROM Articles a WHERE a.id_category = $id_category
Where to put that?! I'm very confused and don't want to brake best practice rules.
Doctrine generated lots of files: ArticleController.php, ArticleType.php (Form), and all the .twig for the views. Same for Category.
In the spirit of giving a direct answer to your question: what you need is a custom Doctrine Repository, in this case an ArticleRepository.
e.g.
ArticleRepository.php
namespace ACME\DemoBundle\Article;
use Doctrine\ORM\EntityRepository;
class ArticleRepository extends EntityRepository
{
public function countForCategory($id)
{
$result= $this->createQueryBuilder('a')
->join('a.category', 'c')
->where('c.category_id = :id')
->setParameter('id', $id)
->select('count(a')
->getQuery()
->getSingleScalarResult();
return $result;
}
}
And then you set your Article entity to use that ArticleRepository
Article.php
/**
* #ORM\Entity
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="ACME\DemoBundle\Entity\ArticleRepository")
*/
class Article
{
/// etc...
You can then get the Repository for the Entity in e.g. your Controller (although as suggested elsewhere you can hide this away in some kind of service to avoid filling your Controller with business logic; it just needs to be reachable somehow from inside your Controller), and run the query:
ArticleController
class ArticleController extends Controller
{
public function countAction($id)
{
$articleRepo = $this->getDoctrine()->getRepository('DemoBundle:Article');
$count = $articleRepo->countForCategory();
//etc...
NB If you really want exactly the output in your example, it may be more efficient to do one query for the whole table, probably in a CategoryRepository, performing a count and grouping by Category. The query would return an array containing Category name and count.
When using Symfony2 + Doctrine2,
don't think (so much) about the database. Think of persistent data objects, so called entities, and let Doctrine handle them. Spend some time on cleanly defining the relations between those objects, though.
forget about the term “MVC”. MVC is a rather abstract concept, things are more complex in reality. Fabian (lead dev of SF) has a nice write-up about this topic: http://fabien.potencier.org/article/49/what-is-symfony2
If you wonder where to put what in your Symfony bundles, read this Cookbook article: http://symfony.com/doc/current/cookbook/bundles/best_practices.html
Create a CategoryManager class in your service layer, and handle any business logic there. You can pass the router to it through dependency injection.
For your example, CategoryManager would have a getUrl(Article $article) method which would use the router instance (that you either injected through __construct or a separate setter method) to generate the Url based on properties of $article, and return it.
This method will ensure that your business logic doesn't pollute the view or controller layers.
Check this link Services Symfony

Can two Eloquent ORM instances manipulate same DB table

I've been working with PHP for years but have never really ventured out of procedural programming except when working with things like IPB and Magento. I'm trying to advance to the next level and get a better understanding of application structures, OOP, and some common PHP frameworks. That being said, I apologize if my question sounds immature or technically incorrect, I'm new to all of this.
Anyway, I was thinking about the structure of a simple forum. Forgetting about categories, tags, users, roles, advanced editors/bbcode, etc for now and just focusing on the topics and posts...
Because a topic is essentially a series of linked posts ordered by their created_at column, is there a necessity for an actual topics table or could one not simply have a parent column in the posts table? Topics would be identified as posts with a parent equal to their own id, null or 0; something that would otherwise be unused.
If that were the db schema, how would it be laid out in the code, and if relevant, Laravel? Could you still create a Topic model? What would be the pros and cons to having two models working from a single table?
Lastly, how would you approach it if you were creating it? Would you use two tables? A pivot table? Something else? Please explain why you would implement it that way.
For the database design, self referencing tables are a valid design pattern and useful in cases of nested hierarchies such as Categories that can contain sub-categories that can also contain sub-categories ect ect... In this case sub-categories are categories that have a parent but there is no other distinction between them.
It's up to you to decide if a Topic and Post is an identical entity with a parent-child relationship. Personally the way I define the objects I don't feel they are.
The topic-post relationship you're describing is probably more of a One to Many relationship with the topic being the owner or maybe even a Many to Many relationship. This depends on the answer to, "can your topic have many posts? Can your posts be part of many topics?"
If you answered yes and no, then it is a One to Many with topics being the parent aka owner in the relationship.
If you answered yes and yes, then you have a Many to Many relationship. In SQL these are represented by a table with two columns that reference id's from two tables.
If you answered no and yes, then you have a One to Many with posts being the parent aka owner in the relationship.
In laravel, depending on the relationship, your models would include a method that looks like this:
One to Many:
class Topic extends Eloquent
{
public function posts()
{
return $this->hasMany('Post');
}
}
Laravel One-to-Many Relationships
Many to Many:
In laravel the term "pivot table" refers to the table with references to the other objects.
class Post extends Eloquent
{
public function topics()
{
return $this->belongsToMany('Topic');
}
}
class Topic extends Eloquent
{
public function posts()
{
return $this->belongsToMany('Post');
}
}
Laravel Many-to-Many
Self referencing model:
For a self referencing parent child relationship like I explained before you could create something like this, as you can see it's just a one-to-many and the many-to-one in the same model.
class Category extends Eloquent
{
public function parent()
{
return $this->belongsTo('Category', 'parent_id');
}
public function children()
{
return $this->hasMany('Category', 'parent_id');
}
}
There is also the Polymorphic Relation.
This is useful when you have the same entity with just a different type. For example in this table you can have an insurance policy for an employee and a manager. The personType column in the insurancePolicies table defines who the insurance policy belongs to.
Image from codecommit.com
Our laravel models in this case would look like:
class InsurancePolicy extends Eloquent
{
public function insurable()
{
return $this->morphTo();
}
}
class Manager extends Eloquent
{
public function insurance()
{
return $this->morphMany('InsurancePolicy', 'person');
}
}
class Employee extends Eloquent
{
public function insurance()
{
return $this->morphMany('InsurancePolicy', 'person');
}
}
Most everything of what I've explained can also be found in the laravel docs

Categories