Laravel-translatable - "call to a member function save() on string" - php

(Updated) I use the laravel-translatable package and trying to insert rows with translations. When trying to save, it gives me the error "call to a member function save() on string".
I loop an object with keys and values, like: "food": "Nourriture",
and inside the loop I do a select of the Translations table:
$translationKey = \App\Translation::select('group', 'key')->where('group',
'global')->where('key', $key)->first();
I don't do exactly as the documentaion, which would have been:
$translationKey = \App\Translation::where('key', $key)->first();
The difference is that I select the columns 'group' and 'key', and I do an extra "where" to specify that group = global. Isthere anything wrong there?
Then I try to check if there is an already existing translation. If not, I insert the translation:
if($translationKey->hasTranslation('fr')) {
continue;
}else{
//insert
$translationRow = $translationKey->translateOrNew('fr')->$key = $value;
$translationRow->save();
}
I use translateOrNew instead of translate , because otherwise I get error: "Creating default object from empty value".
It seems I can't do the ->save() method because it's a string, not a model instance which it should be. So I guess there is something wrong with this line?:
$translationKey = \App\Translation::select('group', 'key')->where('group',
'global')->where('key', $key)->first();
But what is the problem?

I had some mistakes - I needed to select the whole row instead of individual columns:
$translationKey = \App\Translation::where('group', 'global')
->where('key', 'about_us')
->first();
And there were mistakes when saving the translation. My translations_translations table has a "value" column, so this worked:
$translationKey->translateOrNew($locale)->value = $value;
$translationKey->save()

Related

Assign object to object/array in PHP

Long time reader, first time asker. I'm experienced with things like Java/C but PHP is new to me.
I'm having an issue where an assignment doesn't assign to where I'd expect it to.
I'm getting an array from a MySQL database via Eloquent methods, in particular:
$result= TableA::where('tableA.id', '=', $id)
->with('tableB.tableC')
->get();
For reference, printing $result out looks like this:
[{"id":105, /*TableA fields*/, "tableB":null},
{"id":106, /*TableA fields*/, "tableB":null},
{"id":107, /*TableA fields*/, "tableB":{/*tableB fields*/, "tableC":
{"id":104, /*TableC fields*/}}},
{"id":108, /*TableA fields*/, "tableB":{/*tableB fields*/, "tableC":
{"id":105, /*TableC fields*/}}}]
In some cases a TableA tuple will have an associated record in tableB and hence tableC, other times there isn't an associated record in tableB. If there isn't an associated record I want to go through and make a temporary "dummy" record to pass through instead of passing through null. The code I'm using to do so is:
for ($i=0; $i < count($result); $i++)
{
if($result[$i]["tableB"] == null)
{
Log:info($result); //Print line A
$result[$i]["tableB"] = OtherController::makeDummyTableB(); //Assignment line
Log::info($result); //Print line B
Log::info($result[$i]["tableB"]); //Print line C
}
}
The problem is that the assignment line doesn't assign to the "tableB" field in the object/array returned in $result. Printing $result out at print line A and B gives the same result, with "tableB" being null for the first two records. Print line C however gives the output I'm expecting, which is the dummy record I'm creating
{"tableC":{/*TableC fields*/}}
So the assignment is doing something, but it's not assigning to the field in $result that's already there, and instead is assigning it to somewhere else (That doesn't show up when I attempt to display it)
If anyone could let me know what my current code is actually doing, and how to have it do what I'm expecting (replace "tableB":null with "tableB":{"tableC":{/*TableC fields*/}} ) it'd be much appreciated
Eloquent models have a lot of "magic" going on in the background. The fields from the table are loaded into an attributes property, and the relationships are loaded into a relations property.
The issue you're running into is that tableB is a relationship field, not a table field. Its data is stored in the relations property. The relations property, however, is not directly modifiable the way you are attempting. When you attempt to modify it using $result[$i]["tableB"], that code is actually modifying the tableB field in the attributes property.
Then there is the issue of getting the data. When you attempt to read the data directly using $result[$i]["tableB"], it will first look in the attributes property, and if it isn't found there, then it will look in the relations property.
However, when you dump the entire object using Log::info($result);, any data in the relations property overwrites the data in the attributes property. So, after a direct assignment using $result[$i]["tableB"] = 'asdf', Log::info($result) will not show the change (since relations overwrites attributes), whereas Log::info($result[$i]["tableB"]) will show the change (since it looks at attributes first).
So, analyzing your code, we have:
for ($i=0; $i < count($result); $i++) {
if($result[$i]["tableB"] == null) {
// At this point:
// - tableB relation is null
// - tableB attribute does not exist
// This is a full dump, so the relations overwrites the attributes.
// tableB will show null
Log:info($result);
// After this assignment executes:
// - tableB relation will be null
// - tableB attribute will be the dummy object
$result[$i]["tableB"] = OtherController::makeDummyTableB();
// This is a full dump, so relations (null) overwrites the attributes (dummy object).
// tableB will show null
Log::info($result); //Print line B
// This is direct access, which accesses attributes (dummy object) before relations (null).
// tableB will show dummy object
Log::info($result[$i]["tableB"]); //Print line C
}
}
If you really want to go about it this way, you should use the setRelation() method:
$result[$i]->setRelation('tableB', OtherController::makeDummyTableB());
That will specifically set tableB on the relations property, which is what you're trying to do. That should get everything working for you.
Having said that, you may be able to tackle this a different way. If you're using Laravel >= 5.3 and your tableB relationship is a HasOne (5.3+) or a BelongsTo (5.4+) relationship, you can use the withDefault() functionality on the relationship so that it will automatically generate a default model when one does not exist in the database. You can read more about this in the documentation here.
So, your relationship definition would look something like:
public function tableB()
{
return $this->hasOne('App\TableB')->withDefault();
}
Now, when no tableB record exists, the relationship will load with a new empty TableB object, instead of null.
If you need something more than just an empty TableB object, you can pass a function to the withDefault() method, and that function will be used to generate the default object.
Eloquent does not return a plain PHP array, when using the get method it will return an instance of Illuminate\Support\Collection and you cannot simply assign an object like that into a Collection. To do so, you have to convert the collection to an array first:
$result = TableA::where('tableA.id', '=', $id)
->with('tableB.tableC')
->get();
// Convert collection to array
$result = $result->toArray();
for ($i=0; $i < count($result); $i++)
{
if($result[$i]["tableB"] == null)
{
Log:info($result); //Print line A
$result[$i]["tableB"] = OtherController::makeDummyTableB(); //Assignment line
Log::info($result); //Print line B
Log::info($result[$i]["tableB"]); //Print line C
}
}
Now you should have your value correctly assigned.
Try
foreach ($result as $whatever)
{
if($whatever->tableB == null)
{
$whatever->tableB = OtherController::makeDummyTableB();
}
}
The problem is that you can't assign a tableB object to a array field of your tableA model...
Your tableB model most likely (I assume, depends how you built it) is related to your tableA model via a "tableB_id" field. So if you use your assignment, you end up with
$tableA["tableB_id"] //id of the related tableB, in your case null
$tableA["tableB"] //your newly assigned model, which has nothing to do with your relationship, because that should work on tableB_id
Just don't use weird array syntax for relations

Symfony2 - Doctrine query

I have problem with Doctrine query in my Symfony2 project.
That my code:
public function getLender($lender) {
$lender = str_replace("_", " ", $lender);
$this->qb->select('ld, ld.entry, ll.name, ll.logo')
->from('PageMainBundle:LoanDescription', 'ld')
->leftJoin(
'PageMainBundle:loanLender',
'll',
\Doctrine\ORM\Query\Expr\Join::WITH,
'ld.lender = ll.lenderId'
)
->where('ll.name=?1')
->setParameter(1, $lender);
return $this->qb->getQuery()->getResult();
}
When in select section i choose columns it works very well - returns values of columns. unforunelly when I try something like that:
$this->qb->select('ld')
I don't get pure values but sometkhing strange.
How can I get values of all db columns?
This "strange" thing is most probably an LoanDescription collection of object (entity) instances. So to get value of entry field you need to call $entity->getEntry() on this entity object (assuming that you have such method defined in your entity)
OR
You can use getArrayResult instead of getResult and you should get array with valies

Building Eloquent query with "raw" column in column list

I'm using Eloquent to build a query, passing an array of columns to the get() method to specify the column names that I want returning; but I'd also like to add one calculated column
YEAR(CURDATE())-YEAR(`dateOfBirth`) - (DAYOFYEAR(CURDATE()) < DAYOFYEAR(`dateOfBirth`)) as AGE
I know that I can specify parts of a WHERE or HAVING clause as raw, or the entire query as raw if I create it manually; but I'd rather use Eloquent's fluent interface to build the query.
Is there any way I can define this one column in the SELECT list as raw so that Eloquent doesn't wrap it in backticks?
EDIT
Alternatively, is there any way I can define the model, perhaps with a callback, of creating an age property and calculating the value in PHP when the model is populated?
Alternatively, is there any way I can define the model, perhaps with a callback, of creating an age property and calculating the value in PHP when the model is populated?
You want an accessor in your model.
public function getAgeAttribute() {
// do an age calculation on $this->dateOfBirth here
return $age;
}
Calling $model->age would then spit out the result of the calculation.
After examining the Eloquent code, and noting that (with the exception of *) all entries in the fields array that's passed to the query get() method are wrapped in backticks unless they are Query\Expression objects, the solution that I came up with was:
$joins = [
];
$columnnames = [
'id',
'roleId',
'category'
]
$calculatedFields = [
new Illuminate\Database\Query\Expression(
"YEAR(CURDATE())-YEAR(`dateOfBirth`) - (DAYOFYEAR(CURDATE()) < DAYOFYEAR(`dateOfBirth`)) as age",
),
new Illuminate\Database\Query\Expression(
"CONCAT(`forename`, ' ', `surname`) as fullname",
),
];
$modelName = 'User';
$query = (empty($joins)) ?
(new $modelName)->newQuery() :
(new $modelName)->with($this->joins);
$results = $query
->get(
array_merge(
$columnNames,
$calculatedFields
);
);
Posted here for the benefit of anybody else struggling to find any documentation explaining how to do this.

"in" property in Extbase for querying multi-values in TYPO3

The "in" property used in Extbase does not seem to be working for me.
$actor contains an array of Actor model objects. My Movie model and Actor are in m:n relation.
I tried something like this in my Movie Repository:
$query=$this->createQuery();
$query->matching($query->in('actors',$actors));
$result = $query->execute()->toArray();
$result is showing NULL
I tried passing array of actor uids too but that wont work as well:
$query->matching($query->in('actors',[$actor_1_uid,$actor_2_uid]));
There is of course contains but using in should be more convenient.
I don't see any problem in your statement. Just to be clear, a "in" statement must be placed somewhere inside a matching statement, which is correct in your case.
However, you should change your create query for
$query = $this->createQuery();
instead of
$query=$this->create->query();
If you have still no result, I suggest you check the exact the SQL statement executed by extbase, there's a tricky way to do it in TYPO3.
You have to locate the following file in the core:/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
locate the replacePlaceHolders function and add the following codes at the end of the function:
if (strpos( $sqlString, "my_table_name" ) !== false) {
echo $sqlString;
}
I will echo every statement that is being made for the following "my_table_name" table. Of course, never do that in your production server.
I hope it will help!
Cheers,
Olivier
Sorry, but $query->in is the wrong approach. AFAIK it will not work for m:n reations, only for 1:n.
Try something like this, $actors being a query result from the model or the actors repository:
$constraints = array();
foreach ($actors as $actor) {
$constraints[] = $query->contains('actors', $actor->getUid());
}
if (!empty($constraints)) {
$result = $query->matching($query->logicalOr($constraints))->execute();
}
Of course you can use your own array of uids for the loop, then just drop the getUid() method

Codeigniter how to map a class sent to a model

I have a SectorModel with this function:
public function update(Sector $sector) {
$this->db->where('sector_id', $sector->getScetor_id());
return $this->db->update(_SECTOR_, $sector);
}
There are times that I’ll change only the name of the Sector object:
$Sector = new Sector();
$Sector->setSector_name = 'test';
$this->SectorModel->update($Sector);
The generated select looks like:
UPDATE realestate_sector SET sector_name = 'teste', sector_description = NULL
It will update but will set all other properties to NULL because it was not set on my object.
Right now, I have to fill the whole object before sending it.
Is there a way to map the Sector class and update only what was sent on the object?
Thanks in advance for any help.
Sorry for any typos, my English is not good =)
Just loop through all your object's properites and then if any is NULL just drop it with unset.
Here is your model's method edited to achieve that:
public function update(Sector $sector)
{
foreach($sector as $k=>$v)
{
if($v === NULL)
unset($sector->$k)
}
$this->db->where('sector_id', $sector->getScetor_id());
return $this->db->update(_SECTOR_, $sector);
}
Here you can find some info about iterating objects in PHP
The easiest to do this would be to rather use a array - docs here http://codeigniter.com/user_guide/database/active_record.html#update - you just create a array of all the columns with their values that you want to update and perform a $this->db->update('mytable', array('name' => 'test'), array('id' => $id)); call. This will only update the columns you specified in the First array. With the second array acting as your WHERE expression.
The only reason I can think of as to why your other values are being set to NULL is because in your example you create a new instance of the class and the other values must either have been set to nothing or are set to NULL. It would (If this is the case) be better to get a record from the table and then change and values on the populated record and pass that to the function to update.
Hope that helps.

Categories