Asked  7 Months ago    Answers:  5   Viewed   39 times

I have a phone_models, phone_problems, and a phone_model_phone_problem pivot table. The pivot table has an extra column 'price'.

PhoneModel:

class PhoneModel extends Eloquent
{
    public function problems()
    {
        return $this->belongsToMany('RLPhonesEntitiesPhoneProblem')->withPivot('price');
    }
}

PhoneProblem:

class PhoneProblem extends Eloquent
{
    public function models()
    {
        return $this->belongsToMany('PhoneModel')->withPivot('price');
    }
}

What I'm trying to do is get the price of a specific phone with a specific problem.

This is how I have it now but I feel like Laravel has a built in Eloquent feature I can't find to do this in a much simpler way:

$model = $this->phoneService->getModelFromSlug($model_slug);
$problem = $this->phoneService->getProblemFromSlug($problem_slug);

all this does is select the specific model and problem from their slug.

then what I do is with those credentials I get the price like so:

$row = DB::table('phone_model_phone_problem')
->where('phone_model_id', '=', $model->id)
->where('phone_problem', '=', $problem->id)
->first();

so now I can get the price like so $row->price but I feel like there needs to be a much easier and more 'Laravel' way to do this.

 Answers

90

When using Many to Many relationships with Eloquent, the resulting model automatically gets a pivot attribute assigned. Through that attribute you're able to access pivot table columns. Although by default there are only the keys in the pivot object. To get your columns in there too, you need to specify them when defining the relationship:

return $this->belongsToMany('Role')->withPivot('foo', 'bar');

Official Docs

If you need more help the task of configuring the relationships with Eloquent, let me know.

Edit

To query the price do this

$model->problems()->where('phone_problem', $problem->id)->first()->pivot->price
Wednesday, March 31, 2021
 
inieto
answered 7 Months ago
68

You can't do that using with, because it executes separate query.

What you need is simple join. Just translate the query you have to something like:

Posts::join('comments as c', 'posts.id', '=', 'c.id')
    ->selectRaw('posts.*, count(distinct c.id) as numComments')
    ->groupBy('posts.id', 'posts.post_title')
    ->with('user', 'vote', 'tags')
    ->get();

then each post in the collection will have count attribute:

$post->numComments;

However you can make it easier with relations like below:

Though first solution is better in terms of performance (might not be noticeable unless you have big data)

// helper relation
public function commentsCount()
{
    return $this->hasOne('Comment')->selectRaw('posts_id, count(*) as aggregate')->groupBy('posts_id');
}

// accessor for convenience
public function getCommentsCountAttribute()
{
    // if relation not loaded already, let's load it now
    if ( ! array_key_exists('commentsCount', $this->relations)) $this->load('commentsCount');

    return $this->getRelation('commentsCount')->aggregate;
}

This will allow you to do this:

$posts = Posts::with('commentsCount', 'tags', ....)->get();
// then each post:
$post->commentsCount;

And for many to many relations:

public function tagsCount()
{
    return $this->belongsToMany('Tag')->selectRaw('count(tags.id) as aggregate')->groupBy('pivot_posts_id');
}

public function getTagsCountAttribute()
{
    if ( ! array_key_exists('tagsCount', $this->relations)) $this->load('tagsCount');

    $related = $this->getRelation('tagsCount')->first();

    return ($related) ? $related->aggregate : 0;
}

More examples like this can be found here http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/

Wednesday, March 31, 2021
 
eliotlencelot
answered 7 Months ago
62

You are probably going to have to develop your own relationship classes. Ex:

MODEL

public function answers()
{
    $instance = new Response();
    $instance->setSid($this->sid);
    return new QuestionAnswerRelation($instance->newQuery(),$this);
}

RELATIONSHIP

use IlluminateDatabaseEloquentBuilder;
use IlluminateDatabaseEloquentCollection;
use IlluminateDatabaseEloquentRelationsRelation;
use PivotalSurveyModelsAnswer;
use PivotalSurveyModelsCollectionsAnswerCollection;
use PivotalSurveyModelsQuestionInterface;
use PivotalSurveyModelsSurveyInterface;

class QuestionAnswerRelation extends Relation
{

    /**
     * Create a new relation instance.
     *
     * @param  IlluminateDatabaseEloquentBuilder  $query
     * @param  IlluminateDatabaseEloquentModel  $parent
     * @return void
     */
    public function __construct(Builder $query, QuestionInterface $parent)
    {
        $table = $query->getModel()->getTable();
        $this->query = $query
            ->select(array(
                DB::raw($parent->sid.'X'.$parent->gid.'X'.$parent->qid . ' AS value'),
                'id'
            ));

        $this->query = $query;
        $this->parent = $parent;
        $this->related = $query->getModel();
        $this->addConstraints();
    }


    public function addEagerConstraints(array $models)
    {
        parent::addEagerConstraints($models);
    }

    public function initRelation(array $models, $relation)
    {

    }

    public function addConstraints()
    {

    }

    public function match(array $models, Collection $results, $relation)
    {

    }

    public function getResults()
    {
        $results = $this->query->get();
        $answerCollection = new AnswerCollection();

        foreach($results as $result)
        {
            $answer = new Answer($result->toArray());
            $answer->question = $this->parent;
            $answerCollection->add($answer);
        }

        return $answerCollection;
    }

In this case we are using Lime Survey which creates a unique table (note the $instance->setSid() changes the table name) for each of its surveys and a unique column for each of its answer -> question values. ( note $parent->sid.'X'.$parent->gid.'X'.$parent->qid. 'AS value')

Where sid = survey_id, gid = group_id(I think) and qid = question_id

Its was quite irritating.

Note how I reference values from the parent to further develop the query. You should be able to follow a similar route to achieve whatever your heart desires and still maintain the feasibility to use Eloquent.

Saturday, May 29, 2021
 
ALH
answered 5 Months ago
ALH
16

The problem is that your XML has a namespace, which means your XPath does not actually match. Unfortunately there is no default mapping, and XPath does not actually define a good way to handle this internally, so you can't fix it simply by changing the XPath. Instead you need to inform the interpreter how you want to map it in your XPath.

It looks like TouchXML implements support for this via:

- (NSArray *)nodesForXPath:(NSString *)xpath namespaceMappings:(NSDictionary *)inNamespaceMappings error:(NSError **)error;

So you can try something like:

NSDictionary *mappings = [NSDictionary dictionaryWithObject:@"http://tempuri.org/webservices" forKey:@"tempuri"];
[myParser nodesForXPath:@"//tempuri:FundInfo" namespaceMappings:mappings error:&err];
Thursday, July 22, 2021
 
dimitarvp
answered 3 Months ago
74

First, td elements don't have a value attribute, so calling .value on them won't do a thing. Second, the value is actually on the input element, so you need to do mytable.getElementsByTagName('input') instead. Even better would be to give your input fields ids and then use getElementById. This would mean you could alter your HTML without your JS breaking.

Monday, August 30, 2021
 
phirschybar
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :