Problems updating using Eloquent - laravel-4

Right now I'm working on my first update function using Eloquent ORM. Trying to follow the docs, I have this in my model:
public function updateAvailability()
{
$this->active = Input::get('available');
$this->activeDetails = Input::get('availableStatus');
$this->save();
}
which returns:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
and all of this is being called to in my controller as:
public function updateProfile($id)
{
if(Input::get('type')=='availability'){
$availability = User::find($id)->updateAvailability;
}
$name = str_replace(' ', '', Input::get('name'));
return Redirect::to('people/'.$name);
}
Are there some gaps in my understanding of updating in Eloquent? (I'm sure there are). I would love to use ajax to handle it, but I can't seem to find the right resources to get that working.

SOLVED: $availability = User::find($id)->updateAvailability; needed to be changed to $availability = User::find($id)->updateAvailability();. That's all.

Related

Can we use Local Scopes in Symfony?

I wondering about a subject. We can use Local scopes in Laravel but i don't know if for Symfony.
Doc : Laravel Local Scopes
Well, my question is can i use it in Symfony? Is this possible ?
Have a good day
You can do the same with classical methods in your repository.
I can show you an example(using source code from the docs):
public function findAllGreaterThanPrice(int $price, bool $includeUnavailableProducts = false): array
{
// automatically knows to select Products
// the "p" is an alias you'll use in the rest of the query
$qb = $this->createQueryBuilder('p')
->where('p.price > :price')
->setParameter('price', $price)
->orderBy('p.price', 'ASC');
if (!$includeUnavailableProducts) {
$qb->andWhere('p.available = TRUE');
}
$query = $qb->getQuery();
return $query->execute();
// to get just one result:
// $product = $query->setMaxResults(1)->getOneOrNullResult();
}
Here instead of return "$query->execute()", you can return $qb and chain methods will available.
You can do something like that :
$repo->findAllActive()->findAllGreaterThan12();
Here $repo would be the repository injected in your controller.
In both method, you would have just a where and a return of querybuilder.

How to achieve this on laravel 5 eloquent

How can i achieve something like this?
public function getInformation($model) {
$result = $model::with(['province', 'city']);
if($model == 'App\Models\Business') {
$result->with(['businessProvince', 'businessCity']);
}
$result->get();
}
// call the function
$information->getInformation(\App\Models\Business::class);
i'm getting error
Object of class Illuminate\Database\Eloquent\Builder could not be
converted to string
on the sample code above. Any suggestion is really appreciated.
After taking a fourth look $model should be a string, and $result is an Eloquent Builder instance and never an instance of the model class (since a query was started when with was called).
So the $model == 'App\Models\Business' I would change to $model === \App\Models\Business::class but that should not change the outcome.
Are you sure this error comes from this part of the application? Which line specifically?
Original wrong answer.
You are trying to compare the model instance with a string (since $model::with() created a instance of the model class you passed in the $model argument).
You can use the instanceof keyword for comparing an instance with a class name (http://php.net/manual/en/language.operators.type.php).
if($model instanceof \App\Models\Business) {
$result->with(['businessProvince', 'businessCity']);
}
This solved my problem, thank you guys.
public function getInformation($model) {
$result = $model::with(['province', 'city']);
if($model == 'App\Models\Business') {
// my mistake
//$result->with(['businessProvince', 'businessCity']);
$result = $result->with(['businessProvince', 'businessCity']);
}
$result->get();
}

Laravel API APP Many-Many Relationship, how to return specific information in JSON?

I been trying to figure this out for some time now. Basically i got 2 models ' Recipe ', ' Ingredient ' and one Controller ' RecipeController ' .
I'm using Postman to test my API. When i go to my get route which uses RecipeController#getRecipe, the return value is as per the pic below:
Return for Get Route
If i want the return value of the get route to be in the FORMAT of the below pic, how do i achieve this? By this i mean i don't want to see for the recipes: the created_at column, updated_at column and for ingredients: the pivot information column, only want name and amount column information.
Return Value Format I Want
Recipe model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Recipe extends Model
{
protected $fillable = ['name', 'description'];
public function ingredients()
{
return $this->belongsToMany(Ingredient::class,
'ingredient_recipes')->select(array('name', 'amount'));
}
}
Ingredient Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Ingredient extends Model
{
protected $fillable = ['name', 'amount'];
}
RecipeController
<?php
namespace App\Http\Controllers;
use App\Ingredient;
use App\Recipe;
use Illuminate\Http\Request;
class RecipeController extends Controller {
public function postRecipe(Request $request)
{
$recipe = new Recipe();
$recipe->name = $request->input('name');
$recipe->description = $request->input('description');
$recipe->save();
$array_ingredients = $request->input('ingredients');
foreach ($array_ingredients as $array_ingredient) {
$ingredient = new Ingredient();
$ingredient->name = $array_ingredient['ingredient_name'];
$ingredient->amount = $array_ingredient['ingredient_amount'];
$ingredient->save();
$recipe->ingredients()->attach($ingredient->id);
}
return response()->json(['recipe' => $recipe . $ingredient], 201);
}
public function getRecipe()
{
$recipes = Recipe::all();
foreach ($recipes as $recipe) {
$recipe = $recipe->ingredients;
}
$response = [
'recipes' => $recipes
];
return response()->json($response, 200);
}
API Routes:
Route::post('/recipe', 'RecipeController#postRecipe')->name('get_recipe');
Route::get('/recipe', 'RecipeController#getRecipe')->name('post_recipe');
Thanks Guys!
I think your best solution is using Transformer. Using your current implementation what I would recommend is fetching only the needed field in your loop, i.e:
foreach ($recipes as $recipe) {
$recipe = $recipe->ingredients->only(['ingredient_name', 'ingredient_amount']);
}
While the above might work, yet there is an issue with your current implementation because there will be tons of iteration/loop polling the database, I would recommend eager loading the relation instead.
But for the sake of this question, you only need Transformer.
Install transformer using composer composer require league/fractal Then you can create a directory called Transformers under the app directory.
Then create a class called RecipesTransformer, and initialize with:
namespace App\Transformers;
use App\Recipe;
use League\Fractal\TransformerAbstract;
class RecipesTransformer extends TransformerAbstract
{
public function transform(Recipe $recipe)
{
return [
'name' => $recipe->name,
'description' => $recipe->description,
'ingredients' =>
$recipe->ingredients->get(['ingredient_name', 'ingredient_amount'])->toArray()
];
}
}
Then you can use this transformer in your controller method like this:
use App\Transformers\RecipesTransformer;
......
public function getRecipe()
{
return $this->collection(Recipe::all(), new RecipesTransformer);
//or if you need to get one
return $this->item(Recipe::first(), new RecipesTransformer);
}
You can refer to a good tutorial like this for more inspiration, or simply go to Fractal's page for details.
Update
In order to get Fractal collection working since the example I gave would work if you have Dingo API in your project, you can manually create it this way:
public function getRecipe()
{
$fractal = app()->make('League\Fractal\Manager');
$resource = new \League\Fractal\Resource\Collection(Recipe::all(), new RecipesTransformer);
return response()->json(
$fractal->createData($resource)->toArray());
}
In case you want to make an Item instead of collection, then you can have new \League\Fractal\Resource\Item instead. I would recommend you either have Dingo API installed or you can follow this simple tutorial in order to have in more handled neatly without unnecessary repeatition

Adding methods to Eloquent Model in Laravel

I'm a bit confused how I am to add methods to Eloquent models. Here is the code in my controller:
public function show($id)
{
$limit = Input::get('limit', false);
try {
if ($this->isExpand('posts')) {
$user = User::with(['posts' => function($query) {
$query->active()->ordered();
}])->findByIdOrUsernameOrFail($id);
} else {
$user = User::findByIdOrUsernameOrFail($id);
}
$userTransformed = $this->userTransformer->transform($user);
} catch (ModelNotFoundException $e) {
return $this->respondNotFound('User does not exist');
}
return $this->respond([
'item' => $userTransformed
]);
}
And the code in the User model:
public static function findByIdOrUsernameOrFail($id, $columns = array('*')) {
if (is_int($id)) return static::findOrFail($id, $columns);
if ( ! is_null($user = static::whereUsername($id)->first($columns))) {
return $user;
}
throw new ModelNotFoundException;
}
So essentially I'm trying to allow the user to be retrieved by either user_id or username. I want to preserve the power of findOrFail() by creating my own method which checks the $id for an int or string.
When I am retrieving the User alone, it works with no problem. When I expand the posts then I get the error:
Call to undefined method
Illuminate\Database\Query\Builder::findByIdOrUsernameOrFail()
I'm not sure how I would go about approaching this problem.
You are trying to call your method in a static and a non-static context, which won't work. To accomplish what you want without duplicating code, you can make use of Query Scopes.
public function scopeFindByIdOrUsernameOrFail($query, $id, $columns = array('*')) {
if (is_int($id)) return $query->findOrFail($id, $columns);
if ( ! is_null($user = $query->whereUsername($id)->first($columns))) {
return $user;
}
throw new ModelNotFoundException;
}
You can use it exactly in the way you are trying to now.
Also, you can use firstOrFail:
public function scopeFindByIdOrUsernameOrFail($query, $id, $columns = array('*')) {
if (is_int($id)) return $query->findOrFail($id, $columns);
return $query->whereUsername($id)->firstOrFail($columns);
}
Your method is fine, but you're trying to use it in two conflicting ways. The one that works as you intended is the one in the else clause, like you realised.
The reason the first mention doesn't work is because of two things:
You wrote the method as a static method, meaning that you don't call it on an instantiated object. In other words: User::someStaticMethod() works, but $user->someStaticMethod() doesn't.
The code User::with(...) returns an Eloquent query Builder object. This object can't call your static method.
Unfortunately, you'll either have to duplicate the functionality or circumvent it someway. Personally, I'd probably create a user repository with a non-static method to chain from. Another option is to create a static method on the User model that starts the chaining and calls the static method from there.
Edit: Lukas's suggestion of using a scope is of course by far the best option. I did not consider that it would work in this situation.

eloquent morphmany relationship - update and/or save

I have seen the similar Q asked here but did not find any suitable answer and hence asking again. If you know any thread please guide me to it,
I have
Model User and Model Property and both have Address
class Address {
protected $fillable = ['address','city','state','zip'];
public function addressable(){
return $this->morphTo();
}
}//Address
class User extends Eloquent {
protected $fillable = ['first_name','last_name', 'title'];
public function address(){
return $this->morphMany('Address', 'addressable');
}
}//User
class Property extends Eloquent {
protected $fillable = ['name','code'];
public function address(){
return $this->morphMany('Address', 'addressable');
}
}//Property
Is there any way to UpdateIfNotCreate type method for address to update as well associate with User/Property?
Taylor Otwell's official answer,
$account = Account::find(99);
User::find(1)->account()->associate($account)->save();
is NOT working as I am getting an exception
message: "Call to undefined method Illuminate\Database\Query\Builder::associate()"
type: "BadMethodCallException"
The way I have solved the issue is as follows:
$data = Input::all();
if($data['id'] > 0){
$address_id = $data['id']; unset($data['id']);
$address = Address::find($address_id)->update($data);
}//existing
else{
$address = new Address($data);
User::find($user_id)->address()->save($address);
}//add new
I could use the different Routes ( PUT to /update{id} and POST to / )
but in my case both new and existing records are coming to same route ( /update )
Can you guys please recommend the better way to go about this?
Thx,
It's pretty straightforward:
// get only applicable fields
$input = Input::only('address','city','state','zip');
// get existing or instantiate new address
$address = Address::firstOrNew($input);
// associate the address with the user
// btw I would rather call this relation addresses if it's morhpmany
User::find($userId)->addresses()->save($address);
Not sure where you got Taylor's answer, but I don't think it was supposed for this case. It couldn't work anyway.

Resources