I have a house management project and i am trying to execute a complex function that consists of:
Each time i insert a new Bill into the DB, another table will be filled creating a new row for each share of this bill according to the number of flatmates this house has. I managed to insert into the Bills table but the program returns me to the expected page but with no insert into the Shares table. Not sure if logically i'm doing alright. The code bellow is how i tried to retrieve information of the last insert into the Bills table, which should then have its objects properties used into the Shares table. Does someone have any clue on how i can i proceed?
This is my controller function:
public function store(Request $request){
$bill = Bill::create($request->all());
$users = User::where('house_id', Auth::user()->house->id);
$nflatmates = Auth::user()->house->nflatmates;
$shared_amount = $bill->bill_amount / $nflatmates;
foreach($users as $user){
$data = ['user_id'=>$user->id,
'bill_id'=>$bill->id,
'share_amount'=>$shared_amount];
Share::create($data);
}
return redirect('/admin/bills');
}
This is my form blade. I believe the problem doesnt come from here. Just in case.
{!! Form::open(['method'=>'post', 'action'=>'AdminBillsController#store']) !!}
<div class="form-group' has-error' : '' }}">
<div class="col-md-6">
{!! Form::text('description',null,['class'=>'form-control', 'placeholder'=>'Bill Description']) !!}
</div>
</div>
<div class="form-group' has-error' : '' }}">
<div class="col-md-6">
{!! Form::number('bill_amount',null,['class'=>'form-control', 'placeholder'=>'Amount', 'required|between:0,99.99']) !!}
</div>
</div>
<input type="hidden" name="house_id" value="{{Auth::user()->house->id}}">
<br>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Register
</button>
</div>
</div>
{!! Form::close() !!}
These are my relationships:
This is the Share Model
<?php
namespace App;
use App\User;
use App\Bill;
use Illuminate\Database\Eloquent\Model;
class Share extends Model{
protected $fillable = [
'id', 'user_id', 'bill_id', 'share_amount', 'share_status'
];
public function user(){
return $this->belongsTo('App\User');
}
public function bill(){
return $this->belongsTo('App\Bill');
}
}
And this is the Bill Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\House;
use App\User;
use App\Share;
class Bill extends Model{
protected $fillable = [
'id', 'description', 'bill_amount', 'house_id'
];
public function house(){
return $this->belongsTo('App\House');
}
public function share(){
return $this->hasMany('App\Share');
}
}
This is the User Model:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\House;
use App\Role;
use App\Task;
use App\Share;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'id','name', 'email', 'is_active','house_id','role_id','password',
];
protected $hidden = [
'password', 'remember_token',
];
public function house(){
return $this->belongsTo('App\House');
}
public function role(){
return $this->belongsTo('App\Role');
}
public function task(){
return $this->hasMany('App\Task');
}
public function share(){
return $this->hasMany('App\Share');
}
}
And this is the house Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\User;
use App\Bill;
class House extends Model {
protected $fillable = [
'id','house_address', 'house_admin', 'nflatmates'
];
public function user(){
return $this->hasMany('App\User');
}
public function bill(){
return $this->hasMany('App\Bill');
}
}
The thing is that you obviously have oneToMany relationship there, so what you want to do is something like this.
public function store(Request $request){
$bill = Bill::create($request->all());
$users = User::where('house_id', Auth::user()->house->id);
$nflatmates = Auth::user()->house->nflatmates;
$shared_amount = $bill->bill_amount / $nflatmates;
foreach($users as $user){
$data = [
'share_amount' => $shared_amount,
'share_status' => 'XXXXXX'
];
$share = new Share($data);
//Associating user with this share
$share->user()->associate($user);
//Associating bill with this share
$share->bill()->associate($bill);
//Saving share
$share->save();
}
return redirect('/admin/bills');
}
EDIT:
In order for the code above to work, you must have a valid relationships set across your models.
EDIT 2:
I thought that nflatmates was a oneToMany relationship, but it isn't so there is no need for attach function.
We are now creating a Share object and through it's relationships that are defined we are using associate function based on Belongs To Relationships which you can find on official docs, just scroll down a bit.
Related
I have an edit form which should update an activity with selected categories, but the sync function isn't working as expected.
It adds the selected checkboxes to the database but doesn't delete the old values. Resulting in mutliple of the same values in the pivot table.
form code:
<p class="activiteit-categorie">
<label>categorie</label>
#foreach ($categories as $categorie)
<div class="categorie-input">
<input type="checkbox" name="categories[]" value="{{ $categorie->id }}"><label for="categorie">{{$categorie->name}}</label><br>
</div>
#endforeach
</p>
controller code:
if ($request->has('categories')) {
$activiteit->categorie()->sync($request->input('categories'));
} else {
$activiteit->categorie()->detach($request->input('categories'));
}
return redirect()->route('active-overview')->with('message', 'Activiteit is succesvol aangepast!');
models categorie:
namespace App;
use Illuminate\Database\Eloquent\Model;
class categorie extends Model
{
protected $fillable = [
'name'
];
public function activiteit() {
return $this->BelongsToMany('App\activiteit');
}
}
model activiteit:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class activiteit extends Model
{
protected $fillable = [
'name', 'image', 'intro', 'goal', 'traject', 'extra_info', 'duration', 'price'
];
public function categorie() {
return $this->belongsToMany('App\categorie');
}
}
You can try this
class categorie extends Model{
protected $fillable = [
'name'
];
public function activiteit() {
return $this->BelongsToMany('App\activiteit','pivot_table_name','categorie_id','activiteit_id');
}
}
Use like this another Model
if ($request->has('categories')) {
$activiteit->categorie()->sync($request->categories);
} else {
$activiteit->categorie()->detach($request->categories);
}
It works fine for me
So after trying some stuff out I came up with a simple solution to my problem.
I first detach all the categories from the selected activity and then add selected categories to the pivot table.
$activiteit->categorie()->detach();
if ($request->has('categories')) {
$activiteit->categorie()->sync($request->categories);
}
I have Todo and Tbl_leads model and there corresponding tables todos and tbl_leads. When I am trying todo have the lead name it throw me an error.
#This is Tbl_leads model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tbl_leads extends Model
{
//Table Name
protected $table = 'tbl_leads';
//Primary key
public $primaryKey = 'ld_id';
//Timestamps
public $timestamps = true;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'ld_id', 'first_name', 'last_name', 'email',
];
public function tasks() {
$this->hasMany('App\Todo', 'lead_id','ld_id');
}
}
This is Todo Model
<?php
namespace App;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Todo extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'description',
'user_id',
'outcome_id',
'lead_id',
'tasktype_id',
'due_time',
'complete_time',
];
protected $casts = [
'due_time' => 'datetime',
'complete_time',
];
public function lead() {
$this->belongsTo('App\Tbl_leads', 'lead_id');
}
}
This is my index method
public function index()
{
$tasks = Todo::latest()->paginate(5);
return view('taskmanagement.cruds.index',compact('tasks'));
}
this is blade where I want get the the first name from Tbl_leads model
div class="card-body">
<ul class="todo-list" data-widget="todo-list">
#forelse($tasks as $task)
<div class="col-3">
#if(!empty($task->lead_id))
<div>
<small>Related to</small>
</div>
<div class="mt-0">
<a href="#">
<strong class="mx-4">{{$task->lead->first_name}}</strong>
</a>
</div>
#endif
</div>
</div>
</div>
</li>
#empty
<p class="text-center">No Tasks is available</p>
#endforelse
Plz can anyone tells me what I have done wrong. And I know the model name and there primary key slightly different is not set accordingly to laravel. Is my model relationship correct?
Let me clarify few points to you. Firstly you need to put a return in each relationship. For example:
public function lead() {
return $this->belongsTo('App\Tbl_leads', 'lead_id');
}
Next thing you are showing this portion {{$task->lead->first_name}} which is called lazy loading. It means if you are displaying 100 records of Todo then you are querying database 101 times. 1 for Todo and 100 times for lead->first_name. Which is not good. So what you can do in your index method pass your relation in with() so that it will be eager loaded. Means it will become just one or two query or simply a join. So it will be fast. Example of your index method...
public function index()
{
$tasks = Todo::with('lead')->latest()->paginate(5);
return view('taskmanagement.cruds.index',compact('tasks'));
}
Im working on my Laravel project, and have a problem with many-to-many relationship : cannot use "sync" function to store the data in Intermediary Table.
Im following the tutorial in this series : Part 37 - Adding Tag UI/UX
Problem seems to be with this code line : $post->tags()->sync($request->tags, false);
It throws back the error :
BadMethodCallException Call to undefined method App\Post::tags()
I have tried to use attach function instead of sync, does not work.
I dont know which part of code could lead to this issue.
Pls tell me if u guys notice anythings. Tysm !
Post.php (Model)
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table = "posts";
public function category(){
return $this->belongsTo('App\Category');
}
public function user(){
return $this->belongsTo('App\User');
}
public function tag(){
return $this->belongsToMany('App\Tag', 'post_tag');
}
}
Tag.php (Model)
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
protected $table = "tags";
public function post(){
return $this->belongsToMany('App\Post', 'post_tag');
}
}
create_post_tag_table.php (migrations - Intermediary Table)
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePostTagTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('post_tag', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('post_id')->unsigned()->nullable();
$table->foreign('post_id')->references('id')->on('posts');
$table->bigInteger('tag_id')->unsigned()->nullable();
$table->foreign('tag_id')->references('id')->on('tags');
});
}
}
posts.create.blade.php (views - select multiple tags)
<select class="form-control select2-multi" name="tags[]" multiple="multiple" style="width:100%;">
#foreach($tags as $tag)
<option value='{{ $tag->id }}'>{{ $tag->name }}</option>
#endforeach
</select>
PostsController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Post;
use App\Tag;
use App\User;
class PostsController extends Controller
{
public function create()
{
$tags = Tag::all();
return view('posts.create')->with('tags', $tags);
}
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required',
'category_id' => 'required',
]);
$post = new Post;
$post->title = $request->input('title');
$post->description = $request->input('description');
$post->content = $request->input('content');
$post->category_id = $request->input('category_id');
$post->user_id = auth()->user()->id;
$post->status = $request->input('status');
$post->save();
$post->tags()->sync($request->tags, false);
return redirect('/posts')->with('success', 'Post created.');
}
}
You have defined the relationship as tag in your Post model but you are calling tags. You should change it to tags since it is a belongsToMany relationship.
public function tags()
{
return $this->belongsToMany('App\Tag', 'post_tag');
}
I am following tutorial to make blog project on laravel 5.2, i am trying to show the user name from other table (users table). The post properties is shown successfully in the view, but i got Trying to get property of non-object when i try to access property from uses table.
Here is my view:
<div class="blog-post">
<h2 class="blog-post-title">
<a href="/posts/{{$post->id}}">
{{ $post->title }}
</a>
</h2>
<p class="blog-post-meta">
{{ $post->user->name }}
{{ $post->created_at->toFormattedDateString() }}
</p>
{{ $post->body }}
</div><!-- /.blog-post -->
and here is my postscontroller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\User;
use App\Http\Requests;
class PostsController extends Controller
{
//
public function __construct(){
$this->middleware('auth')->except(['index','show']);
}
public function index(){
$posts = Post::latest()->get();
return view('posts.index',compact('posts'));
}
public function show($id){
$post = Post::find($id);
return view('posts.show',compact('post'));
}
public function create(){
return view('posts.create');
}
public function store(){
$this->validate(request(), [
'title' => 'required',
'body' => 'required'
]);
Post::create([
'title' => request('title'),
'body' => request('body'),
'user_id' => auth()->id()
]);
// $post = new Post;
// $post->user_id = auth()->id()
// $post->title = request('title');
// $post->body = request('body');
// $post->save();
return redirect('/');
}
}
here is the post model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//
protected $fillable = ['title','body','user_id'];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments(){
return $this->hasMany(Comment::class);
}
}
user model:
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function posts(){
return $this->hasMany(Post::class);
}
}
As you have said, you are getting this error while accessing user's property through post. That means your $post->user is null or its not an object but array(which has less possibility).
The higher possibility is null issue. Have you checked it? I would prefer you use {{$post->user->name ?? ''}}(supported only in php 7) while showing user's name in view. Which means if $post->user->name is not set then show nothing.
Check if $post->user is null or not. If it is null for any post then the question is, why $post->user can be null? What are the possible causes?
Possible Causes of $post->user to be null are:
Relation is not built correctly. Make sure your posts table has a user_id column as your relationship is defined.
In posts table the user_id is nullable and for for that certain post user_id is null.
The user_id of that post is linked to a non-exist user. And this can be restrict using foreign constrain.
I just finishing my blog, using laravel, and I want to add a comment feature on it, but i got few errors like this, can anyone help me, please??,
sorry for my English, English is not my native language,
Thank you :)
(1/1) ErrorException
Undefined offset: 1
Here is my AdminBlog.php Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AdminBlog extends Model
{
protected $table = 'admin';
protected $fillable = ['title','text','images','slug'];
public function comment(){
return $this->hasMany('App\Comment');
}
}
Comment.php Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
protected $table = 'comment';
protected $fillable = ['name','email','text','post_id'];
public function post(){
return $this->belongsTo('App\AdminBlog');
}
}
BlogController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\AdminBlog;
use App\Comment;
class BlogController extends Controller
{
//Index
public function index(){
$post = AdminBlog::all();
return view('blog/index', compact('post'));
}
//Show
public function show($id){
$post = AdminBlog::findOrFail($id);
$comment = Comment::all();
//dd($comment);
return view('blog/show', ['post' => $post,
'comment' => $comment]);
}
}
show.blade.php
<div class="col-md-12 post-comment-show">
#foreach($post->comment() as $list)
<p>{{ $list->text }}</p>
#foreach
</div>
You should use:
<div class="col-md-12 post-comment-show">
#foreach($post->comment as $list)
<p>{{ $list->text }}</p>
#foreach
</div>
Note that, you have used comment(). Also, you should use a plural name for a hasMany relationship, so comment should be comments.
Also, in your show method, you should use something like the following:
public function show($id)
{
$post = AdminBlog::with('comment')->findOrFail($id);
return view('blog/show', ['post' => $post]);
}
Try the following
$post->comment()->get()
or
$post->comment
when calling a relationship with ()it returns a instance of the query builder and allows you to filter the object. If you remove the call it without () it returns you a collection which is array accessible
Another suggestion if you have many comments you should name your relationship plural.
in this case:
public function comments(){
return $this->hasMany('App\Comment');
}
// Use it like this
$post->comments
You need to change your show function:
public function show($id){
$post = AdminBlog::findOrFail($id);
$comment = Comment::all();
//dd($comment);
return view('blog/show', ['post' => $post,
'comment' => $comment]);
}
TO
public function show($id){
$post = AdminBlog::with('comment')->where('admin.id',$id)->get();
return view('blog/show', compact('post'));
}
Hope this work for you!