Join Table with the id of another join table - mysql

i'm currently trying to build a backend for a project. In this project you will be able to create "ContentElements" that can be used to display content in a page (in my case Sites). Every ContentElement can have multiple options. When a user creates a new Site with an ContentElement (e.g. header) he would enter all options of the element. For example:
"src": "/img/bg.jpg",
"text": "Lorem ipsum..."
In order to save the option's value per page it is used in, i store these values in a separate table (content_elements_sites_values).
My scheme currently looks like this: data scheme
So what i'm currently trying to do is when i get all data associated with the Site i also want to get the data from 'content_elements_sites_values'
$site = $this->Sites->get($id, [
'contain' => ['Templates', 'Colors', 'SiteTypes', 'ContentElements' => [
'ContentElementOptions' => [
'ContentElementsSitesValues'
]
]
],
'conditions' => [
// Just to explain my problem.
'ContentElementsSites.id' => 'ContentElementsSitesValues.content_elements_sites_id'
]
]);
I really don't know if this is even possible or even if my "design" is a total bull***t. But i cannot think of another way to store the filled in data. I'm very open to suggestions of a better way to store this data. Please ask if you need further information in order to help me.
Thank you in advance!
EDIT
I try to explain better what i want to achieve.
Every site can have multiple content_elements of the same type (association is stored in content_elements_sites junction table).
Every content_element can have multiple content_element_options
All content_element_options and content_elements are defined by an Admin.
Another user can create a site and populate it with content_elements and enter content_elements_sites_value for all content_element_options. And as the same content_element (e.g. a paragraph or a list) can have multiple occurrences in the same site, i'll need to store every content_elements_sites_value the user entered.
Thats why i created the link between content_elements_sites and content_element_options.
Currently i'm using this query to get everything expect the value:
$site = $this->Sites->find('all', [
'conditions' => [
'Sites.id' => $id
],
'contain' => ['ContentElements' => [
'sort' => [
'ContentElementsSites.order' => 'ASC'
],
'ContentElementOptions' => [
'ContentElementsSitesValues' => [
'conditions' => [
'ContentElementsSitesValues.content_elements_sites_id' => 'ContentElementsSites.id',
]
]
]
]
]
]);
This results in empty content_elements_sites_values
(int) 1 => object(App\Model\Entity\ContentElementOption) {
'id' => (int) 7,
'content_element_id' => (int) 1,
'name' => 'Test',
'json_name' => 'test',
'option_type_id' => (int) 1,
'content_elements_sites_value' => null,
}
My scheme currently looks like this: data scheme
I'm wondering if this query is even possible. Or if the whole thing is just too flexible.

The way you have defined the relationships signifies that you wish to have a very modular approach so that a content element can be used with multiple sites and a content element option can be used with multiple content elements.
If that is the case, schema direction looks okay with few changes :
1) content_elements_sites_values table can have site_id column directly instead of content_elements_sites_id column as site will be always unique for an entry in that table so the connection id of content_elements_sites isn't required.
2) content_elements_sites_values table can be renamed to content_element_options_values.
3) You can remove id column from content_elements_sites and content_elements_sites_values junction tables.

maybe this is what you are looking for :
$site = $this->Sites->get($id, [
'contain' => ['Templates', 'Colors', 'SiteTypes', 'ContentElements', 'ContentElements.ContentElementOptions','ContentElements.ContentElementOptions.ContentElementsSitesValues'],
....]);
dont forget to define association relationship in model table.

Related

How do I update an eloquent json column from validation data without over writing what's stored in the column?

I'm hoping for a straight-forward solution to do this, but so far I've been coming up empty...
I have a front end vue app/form that sends data back to my laravel backend - I have a controller that validates and saves the request (not looking for feedback on this architecture at the moment unless it actually solves the problem - that's a task for another day...)
I've added a json column called "custom_redeem_fields"
For context, it's to support more flexibility and accepts key/val pairs to use in another field called "custom_redeem_instructions" that has text with delimiters for each of the keys from "custom_redeem_fields", although, I'd prefer to keep from defining these keys statically because the whole point is to be able to add new keys at will. So custom_redeem_instructions will read something like "please visit {•URL•} and enter code {•CODE•}..." and those values will come from the custom_redeem_fields json field.
In the model, I have "custom_redeem_fields" in the fillable array, as well as set as castable to json.
protected $fillable = ['custom_redeem_fields'];
protected $casts = ['custom_redeem_fields' => 'json'];
In the controller, I have ~20 additional columns (not really relevant here, so I've only included two) so I'm trying not to call them out individually beyond their validation rules. The request typically sends one field at a time, so the user can update and save each field as they go. This was working appropriately for all the other fields I had before I added the "custom_redeem_fields.xxxx" to the mix.
$validatedData = $request->validate([
'title' => 'sometimes|required|max:255',
'text' => 'sometimes|required_unless:redeem_type,9|max:255',
'custom_redeem_fields.email' => 'sometimes|email',
'custom_redeem_fields.phone' => ['sometimes', new ValidPhone],
'custom_redeem_fields.code' => 'sometimes',
'custom_redeem_fields.url' => 'sometimes|url'
]);
$ticket = Ticket::find($id)
$ticket->update($validatedData);
Now, with the "custom_redeem_fields.xxxxx" this falls apart - the entire json object stored in "custom_redeem_fields" is overwritten with the most recent update, rather than just updating the key included in the validatedData array. So if I save:
[
"title" => "Monty Pythons Flying Circus"
"text" => "Monty Pythons Flying Circus is a British surreal sketch comedy series created by and starring the comedy group Monty Python, consisting of Graham Chapman, ..."
"custom_redeem_fields" => [
"email" => "bob#example.com",
"phone" => "503.555.5555",
"code" => "1xoicvjq",
"url" => "https://example.com/"
]
]
and then I send:
"custom_redeem_fields" => ["email" => "pat#example.com"]
the custom redeem fields returns:
"custom_redeem_fields" => ["email" => "pat#example.com"]
rather than:
"custom_redeem_fields" => ["email" => "pat#example.com", "phone" => "503.555.5555", "code" => "1xoicvjq", "url" => "https://example.com/"]
It seems that validation rules need json keys to be notated with dot syntax (custom_redeem_fields.url), and eloquent needs arrow syntax (custom_redeem_fields->url), but I'm not sure what's the most straightforward way to transition between the two, which seems very not-laravel, and the documentation is certainly lacking in this department...
Any help would be appreciated.
Thanks!
Wouldn't array_merge() solve your problem, it would overwrite values you provide with the second parameter. If you give it the already existing ones as the first, it would combine the two as you want.
$customRedeemInput = [...];
$model->custom_redeem_fields = array_merge($model->custom_redeem_fields, $customRedeemInput);
$model->save();

yii2 page caching for single posts

I have blog via yii2 and using page caching
'class' => 'yii\filters\PageCache',
'only' => ['view','video'],
'duration' => 900,
'dependency' => [
'class' => 'yii\caching\DbDependency',
'sql' => '?',
],
my post table has primary key as id ;
how to I set sql as separate page cache per post id ?
thank you guy .
Just pass the current ID to it and yes, you can do it, use the enabled property and variation like this
[
'enabled' => Yii::$app->request->isGet && Yii::$app->user->isGuest,
'class' => 'yii\filters\PageCache',
'only' => ['index'],
'duration' => 900,
'variations' => [
Yii::$app->request->get('id')
]
]
This ensures that it will cache only get requests for non logged in users per post ID, you don't want to cache other request than get or logged in users if they can do some things like commenting and see per user data (login name etc.)
You can even use a more advanced config if you like eg. dynamic placeholder with page cache to allow caching even for authenticated users, just check the docs.

How to add and then display images urls from database in Laravel/Backpack CRUD?

I can upload files with special field upload_multiply and then save their names in DB, but how to print them back when editing the record? I use one-to-many relation, database structure like objects and objects_images, where exist object_id, name and url of uploaded image. Im using Laravel 5.4.
Now my code is very typicall:
I have a ObjectCrudController where i build form for my project. I use buildt-in Backpack/CRUD fields like this (for categories):
$this->crud->addField([
'label' => 'Category to display',
'type' => 'select',
'name' => 'category_id',
'entity' => 'category',
'attribute' => 'name',
'model' => 'App\Models\Category',
'wrapperAttributes' => [
'class' => 'form-group col-md-4'
]
]);
But I cant understand how to upload some images and then display their records as miniatures, also i need an ability to remove already uploaded image record. I think there can be used something like html field or view from backpack CRUD, but i don't know where to start.

Laravel/PHPUnit: Assert json element exists without defining the value

I'm sending a post request in a test case, and I want to assert that a specific element, let's say with key 'x' exists in the response. In this case, I can't say seeJson(['x' => whatever]); because the value is unknown to me. and for sure, I can't do it with seeJson(['x']);.
Is there a way to solve this?
If it matters:
Laravel: v5.2.31
PHPUnit: 5.3.4
May it will be helpful for anyone else. You can write this test for your check response json structure
$this->post('/api/login/', [
'email' => 'customer3#example.com',
'password' => '123123123',
])->assertJsonStructure([
'status',
'result' => [
'id',
'email',
'full_name',
],
]);
Although it's not optimal at all, I chose to use this code to test the situation:
$this->post(URL, PARAMS)->see('x');
X is a hypothetical name, and the actual element key has a slim chance of popping up in the rest of the data. otherwise this nasty workaround wouldn't be practical.
UPDATE:
Here's the solution to do it properly:
public function testCaseName()
{
$this->post(route('route.name'), [
'param1' => 1,
'param2' => 10,
], [
'headers_if_any' => 'value'
]);
$res_array = (array)json_decode($this->response->content());
$this->assertArrayHasKey('x', $res_array);
}

yii2 and select2 for tags

I'm using kartik select2 widget like this:
echo Select2::widget([
'model' => $model,
'attribute' => 'script_tags',
'data' => $model->tagList,
'options' => ['multiple' => true,'placeholder' => 'Select states ...'],
'pluginOptions' => [
'tags' => true
],
]);
$model->tagList is array in this format ['id'=>'name'] populated from database.
My question is what is the best why to save it into db table because with custom tags i have response for script_tags like this
[0=>'1', 1=>'5', 2=>'math'],
i need to save new tag in table tag[fields=id, name] and relations for all tags in table tagmap[fields=id, script_id, tag_id]
should i check if is integer save relations in tagmap, if is string save first new tag in tag table then save relation in tagmap
Your approach looks fine, except when somebody explicitly enters a number as tag value. Let's imagine in your tag table ID 1 is 'foo' and ID 5 is 'bar'. There's no way to tell if you got a 'foo,bar,math' or 'foo,5,math'. You could check if those IDs actually exist in your database and act accordingly, but that's probably overkill.
However, I suggest you take a look at some tagging solutions that already exist. I'm pretty happy with 2amigos/yii2-taggable-behavior, but there's also creocoder/yii2-taggable and probably many others.
As an added benefit, 2amigos taggable also stores tag frequencies.
Keep in mind that having an id column in tagmap, which I bet is an autoincrement PK, is bad practice, you should use (script_id, tag_id) as a composite PK.