Hello guys i trying to make product can be buyable only once so i make that
productsTable: id,name,desc,price,onlyOnce(Boolean 0-1)
And i make pivot table too: orders: user_id,product_id
I try this code:
public function showProducts(Category $category, User $user) {
$products = $category->products()->where('onlyOnce', 1)->pluck('id')->toArray();
$userOrders = $user->orders()->pluck('user_Id')->toArray();
$missProducts = array_intersect($userOrders, $products);
$extras = $category->products()->whereNotIn('id', $missProducts)->get();
return view('products.category',compact('extras'));
}
But not work, some ideas how to do that?
(If you need here is my models)
Product.php
class Product extends Model
{
protected $fillable = [];
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function categories() {
return $this->belongsToMany(Category::class);
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function orders(){
return $this->hasMany(Order::class);
}
}
Order.php
class Order extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function product(){
return $this->belongsTo(Product::class);
}
/**
* #return int
*/
public function getTotalPrice() {
$orders = self::with('product')->get();
$total = $orders->pluck('product')->sum('price');
return $total;
}
}
User.php
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name'];
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function orders()
{
return $this->hasMany(Order::class);
}
}
With that code it return empty array i don't know why, so please if you have some ideas how i can fix that or what new code to type answear! Thanks in advice, peace! ;)
You set up your database kind of weird, I would have done it differently, however the following code should retrieve all the products not purchased by a user. Let me know if it works.
$productID = 123;
$userID = 10;
$purchased = $orders->select('product_id')->where('user_id', $userID)->toArray();
$notPurchased = $productsTable->whereNotIn('id',$purchased)->get();
You could then pass $notPurchased to a view, and loop over it with a foreach() loop to get each unpurchased product id.
Related
I am trying to use data from two databases in laravel. Edit: I have added the products controller to the bottom of this post
This is how I test if they are linked:
<p>TEST :{{ $products->ticket->created_at}}</p>
error message:
Undefined variable: products (View: /Applications/MAMP/htdocs/lsapp/resources/views/products/create.blade.php)
Product.php (App)
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
//Table NAME
protected $table = 'products';
//PRIMARY KEY
public $primaryKey = 'id';
//Timestamps
public $timestamps =true;
public function user(){
return $this->belongsTo('App\User');
}
public function products()
{
return $this->hasMany(App\product::class);
}
}
Ticket.php (app)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class product extends Model
{
public function Product(){
return $this->belongsTo(App\Product::class);
}
}
Product Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\product;
use App\Ticket;
class productController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth',['except' => ['index','show']]);
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$products = product::orderBy('created_at','desc')->paginate(10);
//$products = product::where('type','major')->get();
return view('products.index')->with('products',$products);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('products.create');
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request,[
'title' => 'required',
]);
//create product
$product = new product;
$product->title = $request->input('title');
$product->venue = $request->input('venue');
$product->city = $request->input('city');
$product->country = $request->input('country');
$product->description = $request->input('description');
$product->date = $request->input('date');
$product->user_id = auth()->user()->id;
$product->save();
return redirect('/products')->with('success','product Created');
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$product = product::find($id);
return view('products.show')->with('product',$product);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
$product = product::find($id);
if(auth()->user()->id !==$product->user_id){
return redirect('/products')->with('error','unauthorised page');
}
return view('products.edit')->with('product',$product);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'title' => 'required'
]);
$product = product::find($id);
$product->title = $request->input('title');
$product->venue = $request->input('venue');
$product->city = $request->input('city');
$product->country = $request->input('country');
$product->description = $request->input('description');
$product->date = $request->input('date');
$product->user_id = auth()->user()->id;
$product->save();
return redirect('/products')->with('success','product updated');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
$product = product::find($id);
if(auth()->user()->id !==$product->user_id){
return redirect('/products')->with('error','unauthorised page');
}
$product->delete();
return redirect('/products')->with('success','product deleted');
}
}
There are a few problems with your code.
<p>TEST :{{ $products->ticket->created_at}}</p> will never work.
Assuming you are testing in your index page, you need to loop over each product in order to see it's relationship with a ticket.
Do all of your products have a single ticket? If so, then you can just define the single ticket in a config variable. It does not make sense to define a many-to-one relationship in a database.
In your Product model, you have not defined a relationship to the Ticket model. You only have a relationship defined in the Ticket model to the Product model. This means that you can access a product from the ticket, but not the other way around. You need to explicitly define a one-on-one relationship from Product to Ticket in order to use it.
You have defined this function within the Product model.
public function products() {
return $this->hasMany(App\product::class);
}
Why? Do products themselves have many products? I think you may benefit from learning about eloquent relationships. Laracasts is a good resource for learning Laravel from scratch.
Also see Laravel official Eloquent documentation
here are the the two classes with the functions involved
section class has many to many relation with student class
class Section
{
/**
* #ORM\ManyTOMany(targetEntity="Student",inversedBy="sections")
*/
private $students;
public function __construct() {
$this->students = new ArrayCollection();
}
/**
* Add students
*
* #param \Blogger\sectionBundle\Entity\Student $students
* #return Section
*/
public function addStudent(\Blogger\sectionBundle\Entity\Student $students)
{
$this->students[] = $students;
return $this;
}
/**
* Remove students
*
* #param \Blogger\sectionBundle\Entity\Student $students
*/
public function removeStudent(\Blogger\sectionBundle\Entity\Student $students)
{
$this->students->removeElement($students);
}
/**
* Get students
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getStudents()
{
return $this->students;
}
}
and
class Student {
/**
* #ORM\ManyToMany(targetEntity="Section", mappedBy="students")
*/
private $sections;
/**
* #ORM\Column(type="string")
*/
protected $studentId;
public function __construct() {
$this->sections = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add sections
*
* #param \Blogger\sectionBundle\Entity\Section $sections
* #return Student
*/
public function addSection(\Blogger\sectionBundle\Entity\Section $sections)
{
$this->sections[] = $sections;
return $this;
}
/**
* Remove sections
*
* #param \Blogger\sectionBundle\Entity\Section $sections
*/
public function removeSection(\Blogger\sectionBundle\Entity\Section $sections)
{
$this->sections->removeElement($sections);
}
/**
* Get sections
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSections()
{
return $this->sections;
}
}
as in mysql
DELETE from student_section
where student_id = (select student.id from student where student.name="dummy")
And section_id = 1
whats wrong with:
public function removeStudent(Student $student)
{
$this->students->removeElement($student);
}
You can use the generic doctrine command to generate getters and setters.
app/console doctrine:generate:entities NameSpace:Entity
Also you should read about synchronizing ManyToMany bidirectional relationships with Doctrine. Here you can read about the "adders" but the same logic applies to remove methods.
EDIT - After question was updated
If I understood you correctly you want to remove a student from a section when you have the student name. The created student_section is a generated table from Doctrine. You can execute normal PDO statements in your Controllers or Repositories, but I would personaly implement a function in the model to keep it as OOP as possible.
public function removeStudentByName(Student $student)
{
$toRemove = $this->students->filter(function (Student $s) use ($student) {
return ($->getName() == $student->getname());
});
foreach ($toRemove as $student) {
$this->students->remove($student);
}
}
In a controller you can do something like:
//$student, $em is fetched
$section->removeStudentByName($student);
$em->flush();
sorry for my misleading and unclear Question
i found what i was searching for
//in the controller:
$section = $em->getRepository('BloggersectionBundle:Section')->find(2);
$student = $em->getRepository('BloggersectionBundle:Student')->findByStudentId("555555");
$student->removeSections($section);
$em->flush();
and in Student model
public function removeSections(Section $sections)
{
$sections->removeStudent($this);
$this->sections->removeElement($sections);
}
and finally i edited the anotation in both student and section
to cascade remove
* #ORM\ManyToMany(targetEntity="Section", mappedBy="students", cascade={"persist", "remove"})
What's the best way to approach this in Paris ORM?
I have a set of categories and a set of supplier profiles that havw a column called reatured. Currently my class is as follows:
<?php
namespace {
/**
* Class Category
*/
class Category extends ConfettiModel
{
public static $_table = 'supplier_directory_category';
/**
* Returns only top level categories - they have no parent
*
* #return bool
*/
public static function topLevel()
{
return self::where('parent', 0);
}
public static function marketing()
{
return self::where('marketing', 'Yes');
}
public function getTable() {
return self::$_table;
}
/**
* Is this a top level category - has no parent
*
* #return bool
*/
public function isTopLevel()
{
return ($this->parentId == 0);
}
/**
* Associated DirectoryProfile's
*
* #return ORMWrapper
*/
public function profiles()
{
return $this->has_many_through('DirectoryProfile', 'CategoryDirectoryProfile', 'category', 'supplier');
}
}
I'd like to add a new function, featuredProfiles() that allows me to retrieve the same results as profiles(), but in this case I want to restrict it to suppliers with featured = 'Yes'.
I'm not quite sure how to make this happen.
I took a punt and the answer was easier than I anticipated:
public function featuredProfiles() {
return $this->profiles()->where('featured', 'Yes');
}
The where is added as part of the query on the joined table.
i'm trying to test a simple class. I'm following this tutorial( http://code.tutsplus.com/tutorials/testing-laravel-controllers--net-31456 ).
I have this error, while running tests:
Method Mockery_0_App_Interfaces_MealTypeRepositoryInterface::getValidator() does not exist on this mock object
Im using repository structure. So, my controller calls repository and that returns Eloquent's response.
I'm relatively new in php and laravel. And I've started learning to test a few days ago, so I'm sorry for that messy code.
My test case:
class MealTypeControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->mock = Mockery::mock('App\Interfaces\MealTypeRepositoryInterface');
$this->app->instance('App\Interfaces\MealTypeRepositoryInterface' , $this->mock);
}
public function tearDown()
{
Mockery::close();
}
public function testIndex()
{
$this->mock
->shouldReceive('all')
->once()
->andReturn(['mealTypes' => (object)['id' => 1 , 'name' => 'jidlo']]);
$this->call('GET' , 'mealType');
$this->assertViewHas('mealTypes');
}
public function testStoreFails()
{
$input = ['name' => 'x'];
$this->mock
->shouldReceive('getValidator')
->once()
->andReturn(Mockery::mock(['fails' => true]));
$this->mock
->shouldReceive('create')
->once()
->with($input);
$this->call('POST' , 'mealType' , $input ); // this line throws the error
$this->assertRedirectedToRoute('mealType.create');//->withErrors();
$this->assertSessionHasErrors('name');
}
}
My EloquentMealTypeRepository:
Nothing really interesting.
class EloquentMealTypeRepository implements MealTypeRepositoryInterface
{
public function all()
{
return MealType::all();
}
public function find($id)
{
return MealType::find($id);
}
public function create($input)
{
return MealType::create($input);
}
public function getValidator($input)
{
return MealType::getValidator($input);
}
}
My eloquent implementation:
Nothing really interresting,too.
class MealType extends Model
{
private $validator;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'meal_types';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [];
public function meals()
{
return $this->hasMany('Meal');
}
public static function getValidator($fields)
{
return Validator::make($fields, ['name' => 'required|min:3'] );
}
}
My MealTypeRepositoryInterface:
interface MealTypeRepositoryInterface
{
public function all();
public function find($id);
public function create($input);
public function getValidator($input);
}
And finally, My controller:
class MealTypeController extends Controller {
protected $mealType;
public function __construct(MealType $mealType)
{
$this->mealType = $mealType;
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
$mealTypes = $this->mealType->all();
return View::make('mealTypes.index')->with('mealTypes' ,$mealTypes);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$mealType = new MealTypeEloquent;
$action = 'MealTypeController#store';
$method = 'POST';
return View::make('mealTypes.create_edit', compact('mealType' , 'action' , 'method') );
}
/**
* Validator does not work properly in tests.
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$input = ['name' => $request->input('name')];
$mealType = new $this->mealType;
$v = $mealType->getValidator($input);
if( $v->passes() )
{
$this->mealType->create($input);
return Redirect::to('mealType');
}
else
{
$this->errors = $v;
return Redirect::to('mealType/create')->withErrors($v);
}
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
return View::make('mealTypes.show' , ['mealType' => $this->mealType->find($id)]);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
$mealType = $this->mealType->find($id);
$action = 'MealTypeController#update';
$method = 'PATCH';
return View::make('mealTypes.create_edit')->with(compact('mealType' , 'action' , 'method'));
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
$mealType = $this->mealType->find($id);
$mealType->name = \Input::get('name');
$mealType->save();
return redirect('mealType');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
$this->mealType->find($id)->delete();
return redirect('mealType');
}
}
That should be everything. It's worth to say that the application works, just tests are screwed up.
Does anybody know, why is that happening? I cant see a difference between methods of TestCase - testIndex and testStoreFails, why method "all" is found and "getValidator" is not.
I will be thankful for any tips of advices.
Perhaps an aside, but directly relevant to anyone finding this question by its title:
If:
You are getting the error BadMethodCallException: Method Mockery_0_MyClass::myMethod() does not exist on this mock object, and
none of your mocks are picking up any of your subject's methods, and
your classes are being autoloaded, (e.g. using composer)
then before making your mock object, you need to force the loading of that subject, by using this line of code:
spl_autoload_call('MyNamespace\MyClass');
Then you can mock it:
$mock = \Mockery::mock('MyNamespace\MyClass');
In my PHPUnit tests, I often put that first line into the setUpBeforeClass() static function, so it only gets called once and is isolated from tests being added/deleted. So the Test class looks like this:
class MyClassTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
spl_autoload_call('Jodes\MyClass');
}
public function testIt(){
$mock = \Mockery::mock('Jodes\MyClass');
}
}
I have forgotten about this three times now, each time spending an hour or two wondering what on earth the problem was!
I have found a source of this bug in controller.
calling wrong
$v = $mealType->getValidator($input);
instead of right
$v = $this->mealType->getValidator($input);
Well, I think the title explains most of it. Lets get right into it!
Blank Model:
class Blank extends Eloquent
{
protected $table = 'blanks';
protected $softDelete = true;
protected $hidden = array();
/**
* Get associated jobs.
*
* #return mixed
*/
public function jobs()
{
return $this->belongsToMany('Job')->withPivot('status', 'inventory', 'sizes', 'mill', 'po', 'location', 'ordered_at', 'expected_at', 'note')->withTimestamps();
}
/**
* Blanks sizes accessor
*
* #return object
*/
public function getSizesAttribute($value)
{
return json_decode($this->pivot->sizes);
}
/**
* Blanks sizes mutator
*
* #return void
*/
public function setSizesAttribute($value)
{
$this->pivot->attributes['sizes'] = json_encode($this->pivot->sizes);
}
}
Job Model:
class Job extends Eloquent
{
protected $table = 'jobs';
protected $softDelete = true;
protected $hidden = array();
/**
* Get associated blank.
*
* #return mixed
*/
public function blanks()
{
return $this->belongsToMany('Blank')->withPivot('status', 'inventory', 'sizes', 'mill', 'po', 'location', 'ordered_at', 'expected_at', 'note')->withTimestamps();
}
/**
* Blanks sizes accessor
*
* #return object
*/
public function getSizesAttribute($value)
{
return json_decode($this->pivot->sizes);
}
/**
* Blanks sizes mutator
*
* #return void
*/
public function setSizesAttribute($value)
{
$this->pivot->attributes['sizes'] = json_encode($this->pivot->sizes);
}
}
Attaching Code:
$job->blanks()->attach($blank->id,[
'status' => Input::get('status'),
'inventory' => Input::get('inventory'),
//'sizes' => $sizes,
'mill' => Input::get('mill'),
'po' => Input::get('po'),
'location' => Input::get('location'),
'ordered_at' => Carbon::parse(Input::get('ordered_at'))->format('Y-m-d H:i:s'),
'expected_at' => Carbon::parse(Input::get('expected_at'))->format('Y-m-d H:i:s'),
'note' => Input::get('note'),
]);
The mutator is not being called at all.. Any ideas?
Seems like that not possible to do this through ::attach() method.
But maybe you would like to use 'Defining A Custom Pivot Model'
public function newPivot(Model $parent, array $attributes, $table, $exists)
{
return new YourCustomPivot($parent, $attributes, $table, $exists);
}
So, you can define your own pivot class with mutators:
class BlankJobPivot extends Eloquent
{
// ...
/**
* Blanks sizes accessor
*
* #return object
*/
public function getSizesAttribute($value)
{
return json_decode($value);
}
/**
* Blanks sizes mutator
*
* #return void
*/
public function setSizesAttribute($value)
{
$this->attributes['sizes'] = json_encode($value);
return $value; // return for multiple assignment statement: $arr = $pivot->sizes = array(12, 23, 34);
}
}
And than you can use getters:
$blank->jobs[$i]->pivot->sizes; // - ::getSizesAttribute() will called ( I hope :) )
And maybe you will find a way to save / attach through the setSizesAttribute mutator.
Good luck.