Codebase list libdbix-class-perl / 60fbfe7
Documentation improvements cherry-picked from latest Combines 4dbfa426, b339d2ef, 2d707084, f7d6f0e1, b00f9ad4, 372b98a6 and 409a3b1e Peter Rabbitson 9 years ago
13 changed file(s) with 136 addition(s) and 351 deletion(s). Raw diff Collapse all Expand all
1919 Alexander Keusch <cpan@keusch.at>
2020 alexrj: Alessandro Ranellucci <aar@cpan.org>
2121 alnewkirk: Al Newkirk <github@alnewkirk.com>
22 Altreus: Alastair McGowan-Douglas <alastair.mcgowan@opusvl.com>
2223 amiri: Amiri Barksdale <amiribarksdale@gmail.com>
2324 amoore: Andrew Moore <amoore@cpan.org>
2425 Andrew Mehta <Andrew@unitedgames.co.uk>
4243 caldrin: Maik Hentsche <maik.hentsche@amd.com>
4344 castaway: Jess Robinson <castaway@desert-island.me.uk>
4445 chorny: Alexandr Ciornii <alexchorny@gmail.com>
46 cj: C.J. Adams-Collier <cjcollier@cpan.org>
4547 claco: Christopher H. Laco <claco@cpan.org>
4648 clkao: CL Kao <clkao@clkao.org>
4749 Ctrl-O http://ctrlo.com/
5860 dkubb: Dan Kubb <dan.kubb-cpan@onautopilot.com>
5961 dnm: Justin Wheeler <jwheeler@datademons.com>
6062 dpetrov: Dimitar Petrov <mitakaa@gmail.com>
63 Dr^ZigMan: Robert Stone <drzigman@drzigman.com>
6164 dsteinbrunner: David Steinbrunner <dsteinbrunner@pobox.com>
6265 duncan_dmg: Duncan Garland <Duncan.Garland@motortrak.com>
6366 dwc: Daniel Westermann-Clark <danieltwc@cpan.org>
8386 Ian Wells <ijw@cack.org.uk>
8487 idn: Ian Norton <i.norton@shadowcat.co.uk>
8588 ilmari: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
89 ingy: Ingy döt Net <ingy@ingy.net>
8690 initself: Mike Baas <mike@initselftech.com>
8791 ironcamel: Naveed Massjouni <naveedm9@gmail.com>
8892 jasonmay: Jason May <jason.a.may@gmail.com>
207211 wintermute: Toby Corkindale <tjc@cpan.org>
208212 wreis: Wallace Reis <wreis@cpan.org>
209213 xenoterracide: Caleb Cushing <xenoterracide@gmail.com>
214 xmikew: Mike Wisener <xmikew@32ths.com>
210215 yrlnry: Mark Jason Dominus <mjd@plover.com>
211216 zamolxes: Bogdan Lucaciu <bogdan@wiz.ro>
212217 Zefram: Andrew Main <zefram@fysh.org>
1717
1818 * Misc
1919 - Remove warning about potential side effects of RT#79576 (scheduled)
20 - Various doc improvements (GH#35, GH#62, GH#66, GH#70, GH#71, GH#72)
2021 - Depend on newer Moo, to benefit from a safer runtime (RT#93004)
2122 - Fix intermittent failures in the LeakTracer on 5.18+
2223 - Fix failures of t/54taint.t on Windows with spaces in the $^X
2020
2121 __PACKAGE__->add_unique_constraint([qw( name )]);
2222
23 __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd');
23 __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd', 'artistid');
2424
2525 1;
2626
1111 data_type => 'integer',
1212 is_auto_increment => 1
1313 },
14 artist => {
14 artistid => {
1515 data_type => 'integer',
1616 },
1717 title => {
2525
2626 __PACKAGE__->set_primary_key('cdid');
2727
28 __PACKAGE__->add_unique_constraint([qw( title artist )]);
28 __PACKAGE__->add_unique_constraint([qw( title artistid )]);
2929
30 __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist');
31 __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track');
30 __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist', 'artistid');
31 __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track', 'cdid');
3232
3333 1;
1111 data_type => 'integer',
1212 is_auto_increment => 1
1313 },
14 cd => {
14 cdid => {
1515 data_type => 'integer',
1616 },
1717 title => {
2121
2222 __PACKAGE__->set_primary_key('trackid');
2323
24 __PACKAGE__->add_unique_constraint([qw( title cd )]);
24 __PACKAGE__->add_unique_constraint([qw( title cdid )]);
2525
26 __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd');
26 __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd', 'cdid');
2727
2828 1;
3030 }
3131
3232 $schema->populate('Cd', [
33 [qw/title artist/],
33 [qw/title artistid/],
3434 @cds,
3535 ]);
3636
5454 }
5555
5656 $schema->populate('Track',[
57 [qw/cd title/],
57 [qw/cdid title/],
5858 @tracks,
5959 ]);
5252 }
5353 );
5454 while (my $track = $rs->next) {
55 print $track->title . "\n";
55 print $track->title . " (from the CD '" . $track->cd->title
56 . "')\n";
5657 }
5758 print "\n";
5859 }
6970 }
7071 );
7172 my $cd = $rs->first;
72 print $cd->title . "\n\n";
73 print $cd->title . " has the track '$tracktitle'.\n\n";
7374 }
7475
7576 sub get_cds_by_artist {
103104 }
104105 );
105106 my $artist = $rs->first;
106 print $artist->name . "\n\n";
107 print $artist->name . " recorded the track '$tracktitle'.\n\n";
107108 }
108109
109110 sub get_artist_by_cd {
118119 }
119120 );
120121 my $artist = $rs->first;
121 print $artist->name . "\n\n";
122 print $artist->name . " recorded the CD '$cdtitle'.\n\n";
122123 }
17691769 =head2 Formatting DateTime objects in queries
17701770
17711771 To ensure C<WHERE> conditions containing L<DateTime> arguments are properly
1772 formatted to be understood by your RDBMS, you must use the C<DateTime>
1772 formatted to be understood by your RDBMS, you must use the L<DateTime>
17731773 formatter returned by L<DBIx::Class::Storage::DBI/datetime_parser> to format
17741774 any L<DateTime> objects you pass to L<search|DBIx::Class::ResultSet/search>
17751775 conditions. Any L<Storage|DBIx::Class::Storage> object attached to your
1776 L<Schema|DBIx::Class::Schema> provides a correct C<DateTime> formatter, so
1776 L<Schema|DBIx::Class::Schema> provides a correct L<DateTime> formatter, so
17771777 all you have to do is:
17781778
17791779 my $dtf = $schema->storage->datetime_parser;
17921792 C<DateTime> object, which almost never matches the RDBMS expectations.
17931793
17941794 This kludge is necessary only for conditions passed to
1795 L<DBIx::Class::ResultSet/search>, whereas
1796 L<create|DBIx::Class::ResultSet/create>,
1797 L<find|DBIx::Class::ResultSet/find>,
1798 L<DBIx::Class::Row/update> (but not L<DBIx::Class::ResultSet/update>) are all
1795 L<search|DBIx::Class::ResultSet/search> and L<DBIx::Class::ResultSet/find>,
1796 whereas L<create|DBIx::Class::ResultSet/create> and
1797 L<DBIx::Class::Row/update> (but not L<DBIx::Class::ResultSet/update>) are
17991798 L<DBIx::Class::InflateColumn>-aware and will do the right thing when supplied
1800 an inflated C<DateTime> object.
1799 an inflated L<DateTime> object.
18011800
18021801 =head2 Using Unicode
18031802
77 testing a very basic CD database using SQLite, with DBIx::Class::Schema
88 as the database frontend.
99
10 The database consists of the following:
10 The database structure is based on the following rules:
11
12 An artist can have many cds, and each cd belongs to just one artist.
13 A cd can have many tracks, and each track belongs to just one cd.
14
15 The database is implemented with the following:
1116
1217 table 'artist' with columns: artistid, name
13 table 'cd' with columns: cdid, artist, title, year
14 table 'track' with columns: trackid, cd, title
18 table 'cd' with columns: cdid, artistid, title, year
19 table 'track' with columns: trackid, cdid, title
1520
16
17 And these rules exists:
18
19 one artist can have many cds
20 one cd belongs to one artist
21 one cd can have many tracks
22 one track belongs to one cd
23
21 Each of the table's first columns is the primary key; any subsequent
22 keys are foreign keys.
2423
2524 =head2 Installation
2625
27 Install DBIx::Class via CPAN should be sufficient.
26 You'll need to install DBIx::Class via CPAN, and you'll also need to
27 install sqlite3 (not sqlite) if it's not already intalled.
2828
29 =head3 Create the database/tables
29 =head3 The database/tables/data
3030
31 First make and change the directory:
31 Your distribution already comes with a pre-filled SQLite database
32 F<examples/Schema/db/example.db>. You can see it by e.g.
3233
33 mkdir app
34 cd app
35 mkdir db
36 cd db
34 cpanm --look DBIx::Class
3735
38 This example uses SQLite which is a dependency of DBIx::Class, so you
39 shouldn't have to install extra software.
36 If for some reason the file is unreadable on your system, you can
37 recreate it as follows:
4038
41 Save the following into a example.sql in the directory db
39 cp -a <unpacked-DBIC-tarball>/examples/Schema dbicapp
40 cd dbicapp
41 rm db/example.db
42 sqlite3 db/example.db < db/example.sql
43 perl insertdb.pl
4244
43 CREATE TABLE artist (
44 artistid INTEGER PRIMARY KEY,
45 name TEXT NOT NULL
46 );
45 =head3 Testing the database
4746
48 CREATE TABLE cd (
49 cdid INTEGER PRIMARY KEY,
50 artist INTEGER NOT NULL REFERENCES artist(artistid),
51 title TEXT NOT NULL
52 );
47 Enter the example Schema directory
5348
54 CREATE TABLE track (
55 trackid INTEGER PRIMARY KEY,
56 cd INTEGER NOT NULL REFERENCES cd(cdid),
57 title TEXT NOT NULL
58 );
49 cd <unpacked-DBIC-tarball>/examples/Schema
5950
60 and create the SQLite database file:
51 Run the script testdb.pl, which will test that the database has
52 successfully been filled.
6153
62 sqlite3 example.db < example.sql
54 When this script is run, it should output the following:
6355
64 =head3 Set up DBIx::Class::Schema
56 get_tracks_by_cd(Bad):
57 Leave Me Alone
58 Smooth Criminal
59 Dirty Diana
6560
66 Change directory back from db to the directory app:
61 get_tracks_by_artist(Michael Jackson):
62 Billie Jean (from the CD 'Thriller')
63 Beat It (from the CD 'Thriller')
64 Leave Me Alone (from the CD 'Bad')
65 Smooth Criminal (from the CD 'Bad')
66 Dirty Diana (from the CD 'Bad')
6767
68 cd ../
68 get_cd_by_track(Stan):
69 The Marshall Mathers LP has the track 'Stan'.
6970
70 Now create some more directories:
71 get_cds_by_artist(Michael Jackson):
72 Thriller
73 Bad
7174
72 mkdir MyApp
73 mkdir MyApp/Schema
74 mkdir MyApp/Schema/Result
75 mkdir MyApp/Schema/ResultSet
75 get_artist_by_track(Dirty Diana):
76 Michael Jackson recorded the track 'Dirty Diana'.
7677
77 Then, create the following DBIx::Class::Schema classes:
78
79 MyApp/Schema.pm:
80
81 package MyApp::Schema;
82 use base qw/DBIx::Class::Schema/;
83 __PACKAGE__->load_namespaces;
84
85 1;
78 get_artist_by_cd(The Marshall Mathers LP):
79 Eminem recorded the CD 'The Marshall Mathers LP'.
8680
8781
88 MyApp/Schema/Result/Artist.pm:
82 =head3 Discussion about the results
8983
90 package MyApp::Schema::Result::Artist;
91 use base qw/DBIx::Class::Core/;
92 __PACKAGE__->table('artist');
93 __PACKAGE__->add_columns(qw/ artistid name /);
94 __PACKAGE__->set_primary_key('artistid');
95 __PACKAGE__->has_many('cds' => 'MyApp::Schema::Result::Cd');
84 The data model defined in this example has an artist with multiple CDs,
85 and a CD with multiple tracks; thus, it's simple to traverse from a
86 track back to a CD, and from there back to an artist. This is
87 demonstrated in the get_tracks_by_artist routine, where we easily walk
88 from the individual track back to the title of the CD that the track
89 came from ($track->cd->title).
9690
97 1;
98
99
100 MyApp/Schema/Result/Cd.pm:
101
102 package MyApp::Schema::Result::Cd;
103 use base qw/DBIx::Class::Core/;
104 __PACKAGE__->load_components(qw/InflateColumn::DateTime/);
105 __PACKAGE__->table('cd');
106 __PACKAGE__->add_columns(qw/ cdid artist title year/);
107 __PACKAGE__->set_primary_key('cdid');
108 __PACKAGE__->belongs_to('artist' => 'MyApp::Schema::Result::Artist');
109 __PACKAGE__->has_many('tracks' => 'MyApp::Schema::Result::Track');
110
111 1;
112
113
114 MyApp/Schema/Result/Track.pm:
115
116 package MyApp::Schema::Result::Track;
117 use base qw/DBIx::Class::Core/;
118 __PACKAGE__->table('track');
119 __PACKAGE__->add_columns(qw/ trackid cd title /);
120 __PACKAGE__->set_primary_key('trackid');
121 __PACKAGE__->belongs_to('cd' => 'MyApp::Schema::Result::Cd');
122
123 1;
124
125
126 =head3 Write a script to insert some records
127
128 insertdb.pl
129
130 #!/usr/bin/perl
131
132 use strict;
133 use warnings;
134
135 use MyApp::Schema;
136
137 my $schema = MyApp::Schema->connect('dbi:SQLite:db/example.db');
138
139 my @artists = (['Michael Jackson'], ['Eminem']);
140 $schema->populate('Artist', [
141 [qw/name/],
142 @artists,
143 ]);
144
145 my %albums = (
146 'Thriller' => 'Michael Jackson',
147 'Bad' => 'Michael Jackson',
148 'The Marshall Mathers LP' => 'Eminem',
149 );
150
151 my @cds;
152 foreach my $lp (keys %albums) {
153 my $artist = $schema->resultset('Artist')->find({
154 name => $albums{$lp}
155 });
156 push @cds, [$lp, $artist->id];
157 }
158
159 $schema->populate('Cd', [
160 [qw/title artist/],
161 @cds,
162 ]);
163
164
165 my %tracks = (
166 'Beat It' => 'Thriller',
167 'Billie Jean' => 'Thriller',
168 'Dirty Diana' => 'Bad',
169 'Smooth Criminal' => 'Bad',
170 'Leave Me Alone' => 'Bad',
171 'Stan' => 'The Marshall Mathers LP',
172 'The Way I Am' => 'The Marshall Mathers LP',
173 );
174
175 my @tracks;
176 foreach my $track (keys %tracks) {
177 my $cdname = $schema->resultset('Cd')->find({
178 title => $tracks{$track},
179 });
180 push @tracks, [$cdname->id, $track];
181 }
182
183 $schema->populate('Track',[
184 [qw/cd title/],
185 @tracks,
186 ]);
187
188 =head3 Create and run the test scripts
189
190 testdb.pl:
191
192 #!/usr/bin/perl
193
194 use strict;
195 use warnings;
196
197 use MyApp::Schema;
198
199 my $schema = MyApp::Schema->connect('dbi:SQLite:db/example.db');
200 # for other DSNs, e.g. MySQL, see the perldoc for the relevant dbd
201 # driver, e.g perldoc L<DBD::mysql>.
202
203 get_tracks_by_cd('Bad');
204 get_tracks_by_artist('Michael Jackson');
205
206 get_cd_by_track('Stan');
207 get_cds_by_artist('Michael Jackson');
208
209 get_artist_by_track('Dirty Diana');
210 get_artist_by_cd('The Marshall Mathers LP');
211
212
213 sub get_tracks_by_cd {
214 my $cdtitle = shift;
215 print "get_tracks_by_cd($cdtitle):\n";
216 my $rs = $schema->resultset('Track')->search(
217 {
218 'cd.title' => $cdtitle
219 },
220 {
221 join => [qw/ cd /],
222 }
223 );
224 while (my $track = $rs->next) {
225 print $track->title . "\n";
226 }
227 print "\n";
228 }
229
230 sub get_tracks_by_artist {
231 my $artistname = shift;
232 print "get_tracks_by_artist($artistname):\n";
233 my $rs = $schema->resultset('Track')->search(
234 {
235 'artist.name' => $artistname
236 },
237 {
238 join => {
239 'cd' => 'artist'
240 },
241 }
242 );
243 while (my $track = $rs->next) {
244 print $track->title . "\n";
245 }
246 print "\n";
247 }
248
249
250 sub get_cd_by_track {
251 my $tracktitle = shift;
252 print "get_cd_by_track($tracktitle):\n";
253 my $rs = $schema->resultset('Cd')->search(
254 {
255 'tracks.title' => $tracktitle
256 },
257 {
258 join => [qw/ tracks /],
259 }
260 );
261 my $cd = $rs->first;
262 print $cd->title . "\n\n";
263 }
264
265 sub get_cds_by_artist {
266 my $artistname = shift;
267 print "get_cds_by_artist($artistname):\n";
268 my $rs = $schema->resultset('Cd')->search(
269 {
270 'artist.name' => $artistname
271 },
272 {
273 join => [qw/ artist /],
274 }
275 );
276 while (my $cd = $rs->next) {
277 print $cd->title . "\n";
278 }
279 print "\n";
280 }
281
282
283
284 sub get_artist_by_track {
285 my $tracktitle = shift;
286 print "get_artist_by_track($tracktitle):\n";
287 my $rs = $schema->resultset('Artist')->search(
288 {
289 'tracks.title' => $tracktitle
290 },
291 {
292 join => {
293 'cds' => 'tracks'
294 }
295 }
296 );
297 my $artist = $rs->first;
298 print $artist->name . "\n\n";
299 }
300
301 sub get_artist_by_cd {
302 my $cdtitle = shift;
303 print "get_artist_by_cd($cdtitle):\n";
304 my $rs = $schema->resultset('Artist')->search(
305 {
306 'cds.title' => $cdtitle
307 },
308 {
309 join => [qw/ cds /],
310 }
311 );
312 my $artist = $rs->first;
313 print $artist->name . "\n\n";
314 }
315
316
317
318 It should output:
319
320 get_tracks_by_cd(Bad):
321 Dirty Diana
322 Smooth Criminal
323 Leave Me Alone
324
325 get_tracks_by_artist(Michael Jackson):
326 Beat it
327 Billie Jean
328 Dirty Diana
329 Smooth Criminal
330 Leave Me Alone
331
332 get_cd_by_track(Stan):
333 The Marshall Mathers LP
334
335 get_cds_by_artist(Michael Jackson):
336 Thriller
337 Bad
338
339 get_artist_by_track(Dirty Diana):
340 Michael Jackson
341
342 get_artist_by_cd(The Marshall Mathers LP):
343 Eminem
344
345 =head1 Notes
346
347 A reference implementation of the database and scripts in this example
348 are available in the main distribution for DBIx::Class under the
349 directory F<examples/Schema>.
350
351 With these scripts we're relying on @INC looking in the current
352 working directory. You may want to add the MyApp namespaces to
353 @INC in a different way when it comes to deployment.
354
355 The F<testdb.pl> script is an excellent start for testing your database
356 model.
91 Note also that in the get_tracks_by_cd and get_tracks_by_artist
92 routines, the result set is called multiple times with the 'next'
93 iterator. In contrast, get_cd_by_track uses the 'first' result set
94 method, since only one CD is expected to have a specific track.
35795
35896 This example uses L<DBIx::Class::Schema/load_namespaces> to load in the
35997 appropriate L<Result|DBIx::Class::Manual::ResultClass> classes from the
36098 C<MyApp::Schema::Result> namespace, and any required
36199 L<ResultSet|DBIx::Class::ResultSet> classes from the
362 C<MyApp::Schema::ResultSet> namespace (although we created the directory
363 in the directions above we did not add, or need to add, any resultset
364 classes).
100 C<MyApp::Schema::ResultSet> namespace (although we did not add, nor needed
101 any such classes in this example).
365102
366103 =head1 FURTHER QUESTIONS?
367104
8181
8282 __PACKAGE__->table('mydb.mytablename');
8383
84 And load all the Result classes for both / all databases using one
85 L<DBIx::Class::Schema/load_namespaces> call.
84 And load all the Result classes for both / all databases by calling
85 L<DBIx::Class::Schema/load_namespaces>.
8686
8787 =item .. use DBIx::Class across PostgreSQL/DB2/Oracle schemas?
8888
261261 ->on_connect_do("ALTER SESSION SET NLS_SORT = 'BINARY_CI'");
262262 ->on_connect_do("ALTER SESSION SET NLS_SORT = 'GERMAN_CI'");
263263
264 =item .. format a DateTime object for searching?
265
266 L<search|DBIx::Class::ResultSet/search> and L<find|DBIx::Class::ResultSet/find>
267 do not take L<DBIx::Class::InflateColumn> into account, and so your L<DateTime>
268 object will not be correctly deflated into a format your RDBMS expects.
269
270 The L<datetime_parser|DBIx::Class::Storage::DBI/datetime_parser> method on your
271 storage object can be used to return the object that would normally do this, so
272 it's easy to do it manually:
273
274 my $dtf = $schema->storage->datetime_parser;
275 my $rs = $schema->resultset('users')->search(
276 {
277 signup_date => {
278 -between => [
279 $dtf->format_datetime($dt_start),
280 $dtf->format_datetime($dt_end),
281 ],
282 }
283 },
284 );
285
286 With in a Result Class method, you can get this from the
287 L<C<result_source>|DBIx::Class::Row/result_source>.
288
289 my $dtf = $self->result_source->storage->datetime_parser;
290
291 This kludge is necessary only for conditions passed to
292 L<search|DBIx::Class::ResultSet/search> and L<DBIx::Class::ResultSet/find>,
293 whereas L<create|DBIx::Class::ResultSet/create> and L<DBIx::Class::Row/update>
294 (but not L<DBIx::Class::ResultSet/update>) are
295 L<DBIx::Class::InflateColumn>-aware and will do the right thing when supplied
296 an inflated L<DateTime> object.
264297
265298 =back
266299
33213321 "$me.modified" => $user->id,
33223322 });
33233323 }
3324
3325 The alias of L<newly created resultsets|/search> can be altered by the
3326 L<alias attribute|/alias>.
33243327
33253328 =cut
33263329
40854088 as => [qw(some_column dbic_slot)]
40864089
40874090 If you want to individually retrieve related columns (in essence perform
4088 manual prefetch) you have to make sure to specify the correct inflation slot
4091 manual L</prefetch>) you have to make sure to specify the correct inflation slot
40894092 chain such that it matches existing relationships:
40904093
40914094 my $rs = $schema->resultset('Artist')->search({}, {
40924095 # required to tell DBIC to collapse has_many relationships
40934096 collapse => 1,
4094 join => { cds => 'tracks'},
4097 join => { cds => 'tracks' },
40954098 '+columns' => {
40964099 'cds.cdid' => 'cds.cdid',
40974100 'cds.tracks.title' => 'tracks.title',
209209 The length of your column, if it is a column type that can have a size
210210 restriction. This is currently only used to create tables from your
211211 schema, see L<DBIx::Class::Schema/deploy>.
212
213 { size => [ 9, 6 ] }
214
215 For decimal or float values you can specify an ArrayRef in order to
216 control precision, assuming your database's
217 L<SQL::Translator::Producer> supports it.
212218
213219 =item is_nullable
214220
16681668 ) {
16691669 carp_unique 'DateTime objects passed to search() are not supported '
16701670 . 'properly (InflateColumn::DateTime formats and settings are not '
1671 . 'respected.) See "Formatting DateTime objects in queries" in '
1672 . 'DBIx::Class::Manual::Cookbook. To disable this warning for good '
1671 . 'respected.) See ".. format a DateTime object for searching?" in '
1672 . 'DBIx::Class::Manual::FAQ. To disable this warning for good '
16731673 . 'set $ENV{DBIC_DT_SEARCH_OK} to true'
16741674 }
16751675