Ads are created via crop or upload from the article’s edit page from the ads tab.
Every ad belongs to an article and pdf and can have multiple advertisers.
The first advertiser is considered main advertiser and it’s ID is assigned to the ad db record inside advertiser_id field.
After being cropped/uploaded the ad is saved into the ads db table.
Currently the code responsible for saving the ads is in app/Http/Livewire/Ads/Tab.php
This tab component is being called and rendered in the Article’s Edit Page and contains all the logic for saving a cropped ad and properly updating the article’s ad_num and ad_mm_sum fields.
And for uploading an an the responsible code is in app/Http/Livewire/Articles/Edit@submitAdImage which calls: app/Helpers/AdsHelper@uploadAdImage
The ads are edited from the ads editor. Every article edit page contains a button which leads to this editor.
The ad advertises can be created/updated from the ads editor. Each advertiser is later saved in advertisers table, and for the first one we save it’s id inside ads table.
All the consenquent advertisers are saved both inside the advertisers json field inside the ads table and also in the advertisers table. This is implemented in case we wan’t to reuse an adveriser but with slightly diffrent data. So we can autocomplete an advertiser from the UI and only change it’s address or phone for this particular ad and save this data inside the Ad json field instead of directly changing the advertiser and affecting other ads for this advertiser.
Each ad db record contains the ad’s image width and height in pixels. To determine them we use the builin php function getimagesize. So in order to detect them we need to save the ad image first on the filesystem.
We use width/height in pixels to calculate its width/height in milimiters. The current formulas are:
(widthPixels * 25.4)/pixels_per_inch
(heightPixels * 25.4)/pixels_per_inch
The current pixels_per_inch value we use is 150 . It’s defined in: config/processing.php
Other important field for each ad is the col_num field. It describes how much columns the ad is taking in the pdf. This field is later used to calculate it’s full height for invoicing purposes. Usually this field comes from the pdf from which we cropped the ad or is manually provided if the ad image was manually uploaded.
Example php code:
# Save base64 encoded image to the filesystem
$image = imageStore($base64EncodedImage);
# Insert the image data inside the images table
$image = Image::create([
'item_id' => $pdf->id,
'path' => $image->path,
'name' => $image->name,
'type' => Image::getType('ad')
]);
$ad = new Ad;
# Get the image size in pixels
list($width, $height) = getimagesize($b64Ad);
# Get the pixels per inch value
$ppi = config("processing.ftp.ppi");
$ad->image_id = $image->id;
$ad->width = $width;
$ad->height = $height;
# Calculate the width/height in milimeters
$ad->width_mm = round($width * 25.4 / $ppi);
$ad->height_mm = round($height * 25.4 / $ppi);
$ad->col_num = $colNum;
$ad->alias = 'ad-'.\Str::uuid();
$ad->save()
After saving/deleting an ad is important to update the article as well because we save the ads_count and ad_mm_summ for each article inside the article’s db record for optimization purposes so we don’t have to do the agregation later we fetch statistics and invoicing data.
Example php code:
\DB::table('articles')->where('id', $article->id)
->update([
'ads_mm_sum' => $article->calculateAdsMMSum(),
'ads_count' => $article->ads_count+1
]);
Inside \App\Models\Article is the code responsible for counting all of the articles ads milimiters.
public function calculateAdsMMSum() {
$ppi = config("processing.ftp.ppi");
$dimensions = [];
# Get all of the ads from the database.
# It's importang to use ads() instead of just $this->ads because in case of eager loading
# we might miss the newly uploaded ads (ads which was uploaded after the article model was loaded)
$ads = $this->ads()->get();
# Calc. dimensions for each ad
$ads->each(function($ad) use (&$dimensions, $ppi) {
$heightMM = round($ad->height * 25.4 / $ppi);
array_push($dimensions, $heightMM * $ad->col_num);
});
$totalSum = array_sum($dimensions);
# The minimum sum of ads_mm is 250, so if the sum is 0 (in case of no ads) or less then 250 we set them to 250
return $totalSum < 250 ? 250 : $totalSum;
}
The plugin we use for cropping: jquery-select-areas
If the ad is cropped from a pdf we saved it’s coordinates and sizes from the original pdf page image. We need data in order to later crop text from the ad image itself, but since the ad image is not directly procesed in Google Vision we need to calculate the crop areas directly on the original pdf image from which the ad image was cropped.
Example cropped_data contents:
{"x":60,"y":109,"height":73,"width":609,"image":"http://antoan.dev2.transmatico.com/storage/pdfs/1248/pages/page-1.png","clientWidth":753,"clientHeight":1083}
When we have this data and crop text from the ad image we can get the crop selection dimensions and it’s x/y coordinates and recalculate them as they would appear if we made the text crop selection on the original pdf image. Example cropping recalculation:
$adImage.selectAreas({
onChanged: function (event, id, areas) {
if (!areas.length) {
return false;
}
let ratioW2 = cropData.width/$(this).width();
let ratioH2 = cropData.height/$(this).height();
window.area = {};
window.area.x = cropData.x + (window.area.x*ratioW2);
window.area.y = cropData.y + (window.area.y*ratioH2);
window.area.width = ratioW2 * window.area.width;
window.area.height = ratioH2 * window.area.height;
}
Some of the importang fields of the ads table:
| Field | Type | Description |
|---|---|---|
| id | Int | |
| advertiser_id | Int | The id of the main advertiser |
| pdf_id | Int | The id of the pdf the ad was cropped from |
| article_id | Int | The id of the article this ad belongs to |
| alias | String | The alias of the article, used so we can generate it’s own page |
| image_id | Int | The id the of the actual image of the ad |
| width | Int | The ads width in pixels |
| height | Int | the ads height in pixels |
| width_mm | Int | Ad width in milimeters. See above section for calculation details |
| height_mm | Int | Ad height in milimeters. See above section for calculation details |
| col_num | Int | The number of columns the ad is taking on the pdf |
| start_date | Date (Y-m-d) | From which date the ad is supposed to be visible |
| end_date | Date (Y-m-d) | To Which date the ad is supposed to be visible |
| lat | String | Geo Latitude of the ad’s address. The advertiser lat has higher priority and it’s being used |
| lng | String | Geo Longitude of the ad’s address. The advertiser lng has higher priority and it’s being used |
| crop_data | Text (json-encoded) | The crop dimensions, sizes and coordinates from which the ad was cropped |
| Advertisers | Text (json-encoded) | Holds the advertisers data for an article (if there’s more then 1) |
| Placement | Tiny-Integer | Defines how the ad should be displayed on the article’s page. 0=regular, 1=understicial, 2=floating_right |
For full list of the table fields, please check the table in the database