Laravel - Using controller function for two different eloquent models - php

I am currently developing an application for an indy movie production company. The way I have the workflow right now, the user begins by creating a new movie object by entering the movie title and synopsis. From there the user can then add more details such as price, run-time, full-screen/wide-screen, etc. The movie basic (title, synopsis) are in one database table, and the details are in another. I have set up a one-to-one relationship between the two eloquent models. I have also set up a MovieController that allows me to very easily do CRUD operations on the movie basic model, and when I am displaying the movie object to the user, I can display both the basics and details.
What I was wondering was there some way to use the already existent functions in the movie controller to do CRUD operations on the movie details without having to create new functions in the controller? Also is it possible to reuse the views I've created for each corresponding CRUD operation? In other words can I would like
something.dev/cms/create
In one instance to match to creating a new movie (title, synopsis) and in another instance to match to creating the movie detail (price, run-time, full-screen/widescreen) etc. Is this possible? I have provide the code for the two models below:
Movie_basic.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Movie_basic extends Model {
protected $fillable = ['movie_title', 'movie_synopsis'];
protected $guarded = ['id'];
public function details()
{
return $this->hasOne('App\Movie_detail', 'movie_id');
}
public function personnel()
{
return $this->hasMany('App\Movie_personnel', 'movie_id');
}
}
Model_detail.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Movie_detail extends Model {
protected $fillable = ['minutes', 'languages', 'viewer_discretion', 'screen_type', 'price'];
protected $guarded = ['id', 'movie_id'];
public function basics()
{
return $this->belongsTo('App\Movie_basic');
}
}

If I understand you, this might be an answer. (Did not test the code.)
Please note that, that code has been written to show you an example. You will probably want to edit it to make it work and act as you wanted. Maybe you want to use a repository or automate the model instance creating (I did not create new instances), and saving processes. You can use interfaces instead of your models etc...
Here is the service to store the logic.
<?php
use Movie_basic; use Movie_detail;
Class MovieService {
protected $movieBasic;
protected $movieDetail;
public function __construct(Movie_basic $movieBasic, Movie_detail $movieDetail) {
$this->movieBasic = $movieBasic;
$this->movieDetail = $movieDetail;
}
public function createMovie(array $attr) {
// TODO: Move your business logic here.
// E.g
$movie = $this->movieBasic->fill($attr);
$movie->save();
return $movie;
}
public function createMovieDetail(array $movieAttr, array $attributes) {
// TODO: Move your detail logic here.
// E.g.
$basic = $this->createMovie($movieAttr);
$detail = $this->movieDetail->fill($attributes);
$detail->basic()->associate($detail);
$detail->save();
return $detail;
}
}
And here, the controller examples:
<?php
use MovieService;
class MovieController {
public function __construct(MovieService $ms) {
$this->ms = $ms;
}
public function store() {
$this->ms->createMovie($attrToSave);
}
}
<?php
use MovieService;
class MovieDetailController {
public function __construct(MovieService $ms) {
$this->ms = $ms;
}
public function store() {
$this->ms->createMovieDetail($attrToSave);
}
}

Related

Laravel accessing hasMany of hasOne

I have Room, Gallery and Images. I want to associate a gallery to the room and then I want to access the Images of the assigned Gallery using the Room model. I'm new into Laravel, I looked in YouTube lessons, and the documentation but didn't found out a solution for my problem.
Room.php:
class Room extends Model
{
protected $table = 'rooms';
public function gallery()
{
return $this->hasOne('App\Gallery');
}
}
Gallery.php:
class Gallery extends Model
{
protected $table = 'gallery';
public function images()
{
return $this->hasMany('App\Image');
}
public function room()
{
return this->belongsTo('App\Room');
}
}
RoomController.php:
$room = Room::findOrFail($id);
$room_gallery = $room->gallery()->images;
return $room_gallery;
With Eloquent relationships, you access them as properties to access the related model(s) or access the method to query or perform additional manipulation.
Since you want a gallery model and it's related image models, you would access the properties of both:
$room_gallery = $room->gallery->images;
With a HasOne, $room->gallery is essentially equal to $room->gallery()->first(). With HasMany, $gallery->images would be essentially equal to $gallery->images()->get().
However, this may be a case where the HasManyThrough relationship would come in handy.

Laravel Eloquent Model using multiple tables combined into one (no separate models)

I'm working on an REST API with Slim and Eloquent.
I used Medoo DB before. It worked well, but i wanted to remove the static Schema and get more flexible.
I have a DB Table with product information. Problem is i have many more Tables with product information. These are not used for them selves, but only in combination with a product.
So it doesn't make sense to create Eloquent Relationship Classes and a Model for every Sub-Table because they will never be used on there own. It is in fact one table extended over multiple tables.
I know the best would be to change the DB structure and to create one big table, but i cannot do that right now.
So in Medoo i defined a schema Structure with all joinable Tables and a query selecting one product worked. Like i said i want to stay flexible and not define the schema inside the code, but at the moment i can only select data from the main table.
So here is only the Product Model:
<?php
namespace Product\Models;
use Interop\Container\ContainerInterface;
#use Medoo\Medoo;
use \Illuminate\Database\Query\Builder;
use \Illuminate\Database\Eloquent\Model as Model;
use \Illuminate\Database\Capsule\Manager;
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
class Object extends Model
{
protected $database;
public function __construct($database)
{
$this->setTable('product_objects');
$this->database = $database;
}
public function getObjectById($id) {
/*
$data = $this->database
->table('product_objects')
->get($columns)
->first()
;
*/
$data = $this->find($id); // this works (with one table)
// Throw error if no result found.
if (empty($data)) {
throw new \Exception('No object found', 400);
}
return $data;
}
}
// this was just a test
class Freetext extends Model
{
protected $database;
public function __construct($database)
{
$this->setTable('product_freetext');
$this->database = $database;
}
}
Is it possible to do something like:
$data = $this->find($id)->product_freetext->product_table3->product_table4 ...
I solved it so far by inserting a scope method that joins the other tables.
Maybe someone has a better way?
public function scopeObjects($query) {
return $query->join('product_freetext', 'product_freetext.oid', '=', 'product_objects.id');
}
and then
$data = $this->objects()->find($id);

Laravel Eloquent Model inheritance

I'm a new bit who is trying to build an app using Laravel 5.5, and the Eloquent model.
I have two classes: (1)
Customer
and (2)
VIPCustomer which extends Customer.
You may immediately tell VIPCustomer contains all attributes that a customer has, and other extra attributes.
Just to be clear, a customer may not be a VIP, and a VIP must be a customer; The customer may immediately opt-in to be a VIP the first time he shops.
Therefore, I am attempting to do something like this in the database:
Customer:
+------------------------+
|id|name|gender|join_date|
+------------------------+
VIPCustomer:
+----------------------------------+
|customer_id|valid_until|type|point|
+----------------------------------+
(customer_id is a foriegn key referencing Customer.id)
And accordingly, in the model php file:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
}
.
namespace App;
use Illuminate\Database\Eloquent\Model;
class VIPCustomer extends Customer
{
public $incrementing = false;
}
And that's it? I saw there are others saying I should using polymorphic relationship but I don't understand what it means.
In addition, is it possible to do instantiate a new VIP Customer something like this?
$customer = new VIPCustomer;
$customer->name = 'Alice';
$customer->gender = 'F';
$customer->type = 'gold';
$customer->point = 0;
$customer->save();
On top of that, say when the VIP membership ends, is it possible to preserve that person as Customer? Because I'm afraid deleting that person will delete him from both Customer and VIPCustomer tables.
Thank you very much in advance.
Your current VIPCustomer class looks like a class that holds a VIP data, not a subject (a customer). Then so, I would rename it as VIPCustomerData here and make a new VIPCustomer to inherit Customer class instead.
class Customer extends Model
{
protected $table = 'customers';
}
Make sure you define the table name to avoid it being guessed by inheritance. Then tell VIPCustomer to has a relation to VIPCustomerData.
class VIPCustomer extends Customer
{
public function vipData()
{
return $this->hasOne(VIPCustomerData::class, 'customer_id', 'id');
}
}
Now, the problem is whenever you're going to retrieve VIP customers like VIPCustomer::get(), you'll get whole customers instead. So, applying global scope is needed.
class VIPCustomer extends Customer
{
protected static function boot()
{
parent::boot();
static::addGlobalScope('weareviptypeofcustomer', function ($q) {
$q->has('vipData'); // only customers with vip data
});
}
public function vipData()
{
return $this->hasOne(VIPCustomerData::class, 'customer_id', 'id');
}
}
To create a new Customer as VIP, of course 2 queries is needed to insert here. Example,
$vipCustomer = new VIPCustomer;
$vipCustomer->name = 'Alice';
$vipCustomer->gender = 'F';
$vipCustomer->save();
$vipCustomerData = new VIPCustomerData;
$vipCustomerData->type = 'gold';
$vipCustomerData->point = 0;
$vipCustomer->vipData()->save($vipCustomerData);
Example of updating point.
$vipCustomerData = $vipCustomer->vipData; // or $vipCustomer->vipData()->first();
$vipCustomerData->point = 10;
$vipCustomerData->save();
Example of removing VIP status from customer. Of course just delete VIPCustomerData from its table.
$vipCustomer->vipData()->delete();
However, it's better to maintain these subjects as one class if there is no special column to treat each subject differently.
class Customer extends Model
{
protected $table = 'customers';
protected $with = ['vipData']; // always eager load related 'vipData'
protected $appends = ['is_vip']; // append 'is_vip' accessor
public function vipData()
{
return $this->hasOne(static::class, 'customer_id', 'id');
}
public function getIsVipAttribute()
{
return (bool) $this->vipData;
}
}
$customers = Customer::all();
foreach($customers as $customer) {
if ($customer->is_vip) {
// is VIP
} else {
}
}

Polymorphic relations

I have a Link model, which needs a field that refers to either the Page, Redirect or Gallery model. I would like to be able to do something line $link->obj and have that return either the Page, Redirect or Gallery object depending on which one was saved to it.
Polymorphic relations appear to be what I'm looking for, except that I can't seem to get this approach to work.
Current code
<?php
$item = Page::find (1);
$link = new Link ();
$link->linkable ()->save ($item);
$link->save ();
Models
<?php
class Link extends Eloquent
{
protected $table = 'link';
public function linkable ()
{
return $this->morphTo ();
}
}
class Page extends Eloquent
{
protected $table = 'page';
public function linkable ()
{
return $this->morphOne ('Link', 'linkable');
}
}
class Redirect extends Eloquent
{
protected $table = 'redirect';
public function linkable ()
{
return $this->morphOne ('Link', 'linkable');
}
}
class Gallery extends Eloquent
{
protected $table = 'gallery';
public function linkable ()
{
return $this->morphOne ('Link', 'linkable');
}
}
The link database table has linkable_id and linkable_type fields.
I suppose I must be misunderstanding the documentation, because this does not appear to work.
You're close. Assuming you have your database setup correctly, the only issue I see is you calling save() on the morphTo relationship.
The morphTo side of the relationship is the belongsTo side. The belongsTo side does not use the save() method, it uses the associate() method.
So, the code you're looking for should be something like:
$item = Page::find(1);
$link = new Link();
$link->linkable()->associate($item); // associate the belongsTo side
$link->save();
// and to show it worked:
$link->load('linkable');
$page = $link->linkable;
echo get_class($page); // prints "Page"

Like or favorite a post with unique user using Laravel Eloquent

I was creating a like system for my website. in this I wanted one user can only like one time for a post. and a post can be liked by many user. Also many user can like many post.
So if I guess it right, It is a many to many reletionship.
in this context,
I create the following table
... users table:
id
name
....
posts table :
id
post
...post_likes table
id
user id
poost_id
Now I am having the following model for
user :
class User extends SentryUserModel {
public function post_likes()
{
return $this->has_many('Post_like', 'id');
}
}
post :
class Post extends Eloquent {
public function post_likes()
{
return $this->has_many('Post_like', 'id');
}
}
post_like :
class Post_like extends Eloquent {
public function posts()
{
return $this->belongs_to('Post', 'post_id');
}
public function users()
{
return $this->belongs_to('User', 'user_id');
}
}
now when I am going to insert into the database (for post_likes table) I am getting an error called
Illuminate \ Database \ Eloquent \ MassAssignmentException
user_id
Also I want to know is there any way to inset into database like
$user->like()->save($user); ?????
Thank you in advance. Happy coding . \m/
I'll start with a basic issue, firstly you might want to make sure all your tables are lower case (still as a snake case as well), it's not required but it's ultimately how it's expected to be with Laravel so it makes life easier to keep with that. Also a note to the wise, like Class names, database tables are typically in the singular so user instead of users
Secondly yes you can do an insert with $user->post_likes()->save($debate); as your post_likes method on the user class returns has_many.
Thirdly, your design of the Post_like class is a bit off, you could be better off make it like so:
class PostLike extends Eloquent { // note that PostLikes is a more standard naming for a class, they should ideally be camel case names but with all capitals for words
protected $table = 'post_like'; // specifies the table the model uses
public function post() // this should be singular, the naming of a belngs_to method is important as Laravel will do some of the work for you if let it
{
return $this->belongs_to('Post'); // by naming the method 'post' you no longer need to specify the id, Laravel will automatically know from the method name and just adding '_id' to it.
}
public function users()
{
return $this->belongs_to('User');
}
}
Fourthly, your other classes could be better as:
class Post extends Eloquent {
public function post_likes()
{
return $this->has_many('PostLike'); // doesn't require you to specify an id at all
}
}
I can't exactly tell you why you're getting that mass assign error, your post is a bit garbled and doesn't look like you've included the code that actually causes the exception? I have a feeling though is that you're trying to do an insert for multiple database rows at one time but haven't defined a fillable array for PostLike such as with here: http://four.laravel.com/docs/eloquent#mass-assignment
According to Laravel 5:
User Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
public function post_likes()
{
return $this->hasMany('App\PostLike');
}
}
Post Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model {
public function post_likes()
{
return $this->hasMany('App\PostLike');
}
}
PostLike Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class PostLike extends Model {
protected $table = 'post_like';
public function posts()
{
return $this->belongsTo('App\Post');
}
public function users()
{
return $this->belongsTo('App\User');
}
}
and if you want to save the post_like data then:
$inputs['post_id'] = 1;
$inputs['user_id'] = 4;
$post_like = PostLike::create($inputs);
Hope this helps!!

Categories